Source Code
Overview
S Balance
S Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
SonicHarvester
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IERC20, IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {OwnableOperable} from "./OwnableOperable.sol";
import {IHarvestable, IMagpieRouter, IOracle} from "./Interfaces.sol";
/**
* @title Collects rewards from strategies and swaps them for ARM liquidity tokens.
* @author Origin Protocol Inc
*/
contract SonicHarvester is Initializable, OwnableOperable {
using SafeERC20 for IERC20;
enum SwapPlatform {
Magpie
}
/// @notice All reward tokens are swapped to the ARM's liquidity asset.
address public immutable liquidityAsset;
/// @notice Mapping of strategies that rewards can be collected from
mapping(address => bool) public supportedStrategies;
/// @notice Oracle contract used to validate swap prices
address public priceProvider;
/// @notice Maximum allowed slippage denominated in basis points. Example: 300 == 3% slippage
uint256 public allowedSlippageBps;
/// @notice Address receiving rewards proceeds. Initially this will be the ARM contract,
/// later this could be a Dripper contract that eases out rewards distribution.
address public rewardRecipient;
/// @notice The address of the Magpie router that performs swaps
address public magpieRouter;
uint256[45] private _gap;
event SupportedStrategyUpdate(address strategy, bool isSupported);
event RewardTokenSwapped(
address indexed rewardToken,
address indexed swappedInto,
SwapPlatform swapPlatform,
uint256 amountIn,
uint256 amountOut
);
event RewardsCollected(address[] indexed strategy, address[][] rewardTokens, uint256[][] amounts);
event RewardRecipientUpdated(address rewardRecipient);
event AllowedSlippageUpdated(uint256 allowedSlippageBps);
event PriceProviderUpdated(address priceProvider);
event MagpieRouterUpdated(address router);
error SlippageError(uint256 actualBalance, uint256 minExpected);
error BalanceMismatchAfterSwap(uint256 actualBalance, uint256 minExpected);
error InvalidSwapPlatform(SwapPlatform swapPlatform);
error UnsupportedStrategy(address strategyAddress);
error InvalidSwapRecipient(address recipient);
error InvalidFromAsset(address fromAsset);
error InvalidFromAssetAmount(uint256 fromAssetAmount);
error InvalidToAsset(address toAsset);
error EmptyLiquidityAsset();
error EmptyMagpieRouter();
error EmptyRewardRecipient();
error InvalidDecimals();
error InvalidAllowedSlippage(uint256 allowedSlippageBps);
constructor(address _liquidityAsset) {
if (_liquidityAsset == address(0)) revert EmptyLiquidityAsset();
if (IERC20Metadata(_liquidityAsset).decimals() != 18) revert InvalidDecimals();
liquidityAsset = _liquidityAsset;
}
/// @notice
function initialize(
address _priceProvider,
uint256 _allowedSlippageBps,
address _rewardRecipient,
address _magpieRouter
) external initializer onlyOwner {
_setPriceProvider(_priceProvider);
_setAllowedSlippage(_allowedSlippageBps);
_setRewardRecipient(_rewardRecipient);
_setMagpieRouter(_magpieRouter);
}
/**
* @notice Collect reward tokens from each strategy into this harvester contract.
* Can be called by anyone.
* @param _strategies Addresses of the supported strategies to collect rewards from
*/
function collect(address[] calldata _strategies)
external
returns (address[][] memory rewardTokens, uint256[][] memory amounts)
{
rewardTokens = new address[][](_strategies.length);
amounts = new uint256[][](_strategies.length);
for (uint256 i = 0; i < _strategies.length; ++i) {
if (!supportedStrategies[_strategies[i]]) {
revert UnsupportedStrategy(_strategies[i]);
}
(rewardTokens[i], amounts[i]) = IHarvestable(_strategies[i]).collectRewards();
}
emit RewardsCollected(_strategies, rewardTokens, amounts);
}
/**
* @notice Swaps the reward token to the ARM's liquidity asset using a DEX aggregator.
* @dev The initial implementation only supports the Magpie DEX aggregator.
* The fromAsset, fromAssetAmount, toAsset and recipient are validated against the
* platform specific swap data.
* @param swapPlatform The swap platform to use. Currently only Magpie is supported.
* @param fromAsset The address of the reward token to swap from.
* @param fromAssetAmount The amount of reward tokens to swap from.
* @param data aggregator specific data. eg Magpie's swapWithMagpieSignature data
*/
function swap(SwapPlatform swapPlatform, address fromAsset, uint256 fromAssetAmount, bytes calldata data)
external
onlyOperatorOrOwner
returns (uint256 toAssetAmount)
{
uint256 recipientAssetBefore = IERC20(liquidityAsset).balanceOf(rewardRecipient);
// Validate the swap data and do the swap
toAssetAmount = _doSwap(swapPlatform, fromAsset, fromAssetAmount, data);
// Check the recipient of the swap got the reported amount of liquidity assets
uint256 recipientAssets = IERC20(liquidityAsset).balanceOf(rewardRecipient) - recipientAssetBefore;
if (recipientAssets < toAssetAmount) {
revert BalanceMismatchAfterSwap(recipientAssets, toAssetAmount);
}
emit RewardTokenSwapped(fromAsset, liquidityAsset, swapPlatform, fromAssetAmount, toAssetAmount);
// If there is no price provider, we exit early
if (priceProvider == address(0)) return toAssetAmount;
// Get the Oracle price from the price provider
uint256 oraclePrice = IOracle(priceProvider).price(fromAsset);
// Calculate the minimum expected amount from the max slippage from the Oracle price
uint256 minExpected = (fromAssetAmount * (1e4 - allowedSlippageBps) * oraclePrice) // max allowed slippage
/ 1e4 // fix the max slippage decimal position
/ 1e18; // and oracle price decimals position
if (toAssetAmount < minExpected) {
revert SlippageError(toAssetAmount, minExpected);
}
}
/// @dev Platform specific swap logic
function _doSwap(SwapPlatform swapPlatform, address fromAsset, uint256 fromAssetAmount, bytes memory data)
internal
returns (uint256 toAssetAmount)
{
if (swapPlatform == SwapPlatform.Magpie) {
address parsedRecipient;
address parsedFromAsset;
address parsedToAsset;
uint256 fromAssetAmountShift;
uint256 fromAssetAmountOffset;
uint256 parsedFromAssetAmount;
assembly {
// Length: 32 bytes (n padded).
// then there is 4 bytes of unknown data
// so the data offset of 32 + 4 = 36 bytes
// Load the swap recipient address (20 bytes) starting at offset 36
parsedRecipient := mload(add(data, 36))
// Shift right by 96 bits (32 - 20 bytes) to get only the 20 bytes
parsedRecipient := shr(96, parsedRecipient)
// Load the swap from asset address (20 bytes) starting at offset 36 + 20
parsedFromAsset := mload(add(data, 56))
// Shift right by 96 bits (32 - 20 bytes) to get only the 20 bytes
parsedFromAsset := shr(96, parsedFromAsset)
// Load the swap from asset address (20 bytes) starting at offset 36 + 20 + 20
parsedToAsset := mload(add(data, 76))
// Shift right by 96 bits (32 - 20 bytes) to get only the 20 bytes
parsedToAsset := shr(96, parsedToAsset)
// Load the fromAssetAmount
// load the first byte which is the number of bytes to shift fromAssetAmount.
// ie 32 bytes - fromAssetAmount length in bytes
// For example, 1e18 is 8 bytes long so needs to be shifted 32 - 8 = 24 bytes = 196 bits
fromAssetAmountShift := mload(add(data, 105))
// Shift right by 248 bits (32 - 31 bytes) to get only the 1 byte
fromAssetAmountShift := shr(248, fromAssetAmountShift)
// load the next two bytes which is the position of fromAssetAmount in the data
fromAssetAmountOffset := mload(add(data, 106))
// Shift right by 240 bits (32 - 30 bytes) to get only the 2 bytes
fromAssetAmountOffset := shr(240, fromAssetAmountOffset)
// Subtract 36 bytes as the position are different to calldata used by Magpie
fromAssetAmountOffset := sub(fromAssetAmountOffset, 36)
// load the amountIn from the offset
parsedFromAssetAmount := mload(add(data, fromAssetAmountOffset))
parsedFromAssetAmount := shr(fromAssetAmountShift, parsedFromAssetAmount)
}
if (rewardRecipient != parsedRecipient) revert InvalidSwapRecipient(parsedRecipient);
if (fromAsset != parsedFromAsset) revert InvalidFromAsset(parsedFromAsset);
if (liquidityAsset != parsedToAsset) revert InvalidToAsset(parsedToAsset);
if (fromAssetAmount != parsedFromAssetAmount) revert InvalidFromAssetAmount(parsedFromAssetAmount);
// Approve the Magpie Router to spend the fromAsset
IERC20(fromAsset).approve(magpieRouter, fromAssetAmount);
// Call the Magpie router to do the swap
toAssetAmount = IMagpieRouter(magpieRouter).swapWithMagpieSignature(data);
} else {
revert InvalidSwapPlatform(swapPlatform);
}
}
////////////////////////////////////////////////////
/// Admin Functions
////////////////////////////////////////////////////
/// @notice Set the address of the price provider contract providing Oracle prices.
function setPriceProvider(address _priceProvider) external onlyOwner {
_setPriceProvider(_priceProvider);
}
/// @notice Set the maximum allowed slippage on swaps from the Oracle price.
/// @param _allowedSlippageBps denominated in basis points. Example: 300 == 3% slippage
function setAllowedSlippage(uint256 _allowedSlippageBps) external onlyOwner {
_setAllowedSlippage(_allowedSlippageBps);
}
/// @notice Set a new reward recipient that receives liquidity assets after
/// rewards tokens are swapped.
function setRewardRecipient(address _rewardRecipient) external onlyOwner {
_setRewardRecipient(_rewardRecipient);
}
/// @notice Flags a strategy as supported or not supported.
/// @param _strategyAddress Address of the strategy contract.
/// @param _isSupported Bool marking strategy as supported or not supported
function setSupportedStrategy(address _strategyAddress, bool _isSupported) external onlyOwner {
supportedStrategies[_strategyAddress] = _isSupported;
emit SupportedStrategyUpdate(_strategyAddress, _isSupported);
}
/// @notice Set the MagpieRouter address
/// @param _router New router address
function setMagpieRouter(address _router) external onlyOwner {
_setMagpieRouter(_router);
}
function _setPriceProvider(address _priceProvider) internal {
priceProvider = _priceProvider;
emit PriceProviderUpdated(_priceProvider);
}
function _setAllowedSlippage(uint256 _allowedSlippageBps) internal {
if (_allowedSlippageBps > 1000) revert InvalidAllowedSlippage(_allowedSlippageBps);
allowedSlippageBps = _allowedSlippageBps;
emit AllowedSlippageUpdated(_allowedSlippageBps);
}
function _setRewardRecipient(address _rewardRecipient) internal {
if (_rewardRecipient == address(0)) revert EmptyRewardRecipient();
rewardRecipient = _rewardRecipient;
emit RewardRecipientUpdated(_rewardRecipient);
}
function _setMagpieRouter(address _router) internal {
if (_router == address(0)) revert EmptyMagpieRouter();
magpieRouter = _router;
emit MagpieRouterUpdated(_router);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import {Ownable} from "./Ownable.sol";
/**
* @title Base contract that provides ownership and operational control
* @author Origin Protocol Inc
*/
contract OwnableOperable is Ownable {
/// @notice The account that can request and claim withdrawals.
address public operator;
uint256[49] private _gap;
event OperatorChanged(address newAdmin);
function _initOwnableOperable(address _operator) internal {
_setOperator(_operator);
}
/// @notice Set the account that can request and claim withdrawals.
/// @param newOperator The address of the new operator.
function setOperator(address newOperator) external onlyOwner {
_setOperator(newOperator);
}
function _setOperator(address newOperator) internal {
operator = newOperator;
emit OperatorChanged(newOperator);
}
modifier onlyOperatorOrOwner() {
require(msg.sender == operator || msg.sender == _owner(), "ARM: Only operator or owner can call this function.");
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function decimals() external view returns (uint8);
event Transfer(address indexed from, address indexed to, uint256 value);
}
interface IOethARM {
function token0() external returns (address);
function token1() external returns (address);
function owner() external returns (address);
/**
* @notice Swaps an exact amount of input tokens for as many output tokens as possible.
* msg.sender should have already given the ARM contract an allowance of
* at least amountIn on the input token.
*
* @param inToken Input token.
* @param outToken Output token.
* @param amountIn The amount of input tokens to send.
* @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
* @param to Recipient of the output tokens.
*/
function swapExactTokensForTokens(
IERC20 inToken,
IERC20 outToken,
uint256 amountIn,
uint256 amountOutMin,
address to
) external;
/**
* @notice Uniswap V2 Router compatible interface. Swaps an exact amount of
* input tokens for as many output tokens as possible.
* msg.sender should have already given the ARM contract an allowance of
* at least amountIn on the input token.
*
* @param amountIn The amount of input tokens to send.
* @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
* @param path The input and output token addresses.
* @param to Recipient of the output tokens.
* @param deadline Unix timestamp after which the transaction will revert.
* @return amounts The input and output token amounts.
*/
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
/**
* @notice Receive an exact amount of output tokens for as few input tokens as possible.
* msg.sender should have already given the router an allowance of
* at least amountInMax on the input token.
*
* @param inToken Input token.
* @param outToken Output token.
* @param amountOut The amount of output tokens to receive.
* @param amountInMax The maximum amount of input tokens that can be required before the transaction reverts.
* @param to Recipient of the output tokens.
*/
function swapTokensForExactTokens(
IERC20 inToken,
IERC20 outToken,
uint256 amountOut,
uint256 amountInMax,
address to
) external;
/**
* @notice Uniswap V2 Router compatible interface. Receive an exact amount of
* output tokens for as few input tokens as possible.
* msg.sender should have already given the router an allowance of
* at least amountInMax on the input token.
*
* @param amountOut The amount of output tokens to receive.
* @param amountInMax The maximum amount of input tokens that can be required before the transaction reverts.
* @param path The input and output token addresses.
* @param to Recipient of the output tokens.
* @param deadline Unix timestamp after which the transaction will revert.
* @return amounts The input and output token amounts.
*/
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function setOwner(address newOwner) external;
function transferToken(address token, address to, uint256 amount) external;
// From OethLiquidityManager
function requestWithdrawal(uint256 amount) external returns (uint256 requestId, uint256 queued);
function claimWithdrawal(uint256 requestId) external;
function claimWithdrawals(uint256[] calldata requestIds) external;
}
interface ILiquidityProviderARM is IERC20 {
function previewDeposit(uint256 assets) external returns (uint256 shares);
function deposit(uint256 assets) external returns (uint256 shares);
function deposit(uint256 assets, address liquidityProvider) external returns (uint256 shares);
function previewRedeem(uint256 shares) external returns (uint256 assets);
function requestRedeem(uint256 shares) external returns (uint256 requestId, uint256 assets);
function claimRedeem(uint256 requestId) external returns (uint256 assets);
function totalAssets() external returns (uint256 assets);
function convertToShares(uint256 assets) external returns (uint256 shares);
function convertToAssets(uint256 shares) external returns (uint256 assets);
function lastTotalAssets() external returns (uint256 assets);
}
interface ICapManager {
function postDepositHook(address liquidityProvider, uint256 assets) external;
}
interface LegacyAMM {
function transferToken(address tokenOut, address to, uint256 amount) external;
}
interface IOriginVault {
function mint(address _asset, uint256 _amount, uint256 _minimumOusdAmount) external;
function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;
function requestWithdrawal(uint256 amount) external returns (uint256 requestId, uint256 queued);
function claimWithdrawal(uint256 requestId) external returns (uint256 amount);
function claimWithdrawals(uint256[] memory requestIds)
external
returns (uint256[] memory amounts, uint256 totalAmount);
function addWithdrawalQueueLiquidity() external;
function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;
function governor() external view returns (address);
function dripper() external view returns (address);
function withdrawalQueueMetadata()
external
view
returns (uint128 queued, uint128 claimable, uint128 claimed, uint128 nextWithdrawalIndex);
function withdrawalRequests(uint256 requestId)
external
view
returns (address withdrawer, bool claimed, uint40 timestamp, uint128 amount, uint128 queued);
function claimDelay() external view returns (uint256);
}
interface IGovernance {
enum ProposalState {
Pending,
Active,
Canceled,
Defeated,
Succeeded,
Queued,
Expired,
Executed
}
function state(uint256 proposalId) external view returns (ProposalState);
function proposalSnapshot(uint256 proposalId) external view returns (uint256);
function proposalDeadline(uint256 proposalId) external view returns (uint256);
function proposalEta(uint256 proposalId) external view returns (uint256);
function votingDelay() external view returns (uint256);
function castVote(uint256 proposalId, uint8 support) external returns (uint256 balance);
function queue(uint256 proposalId) external;
function execute(uint256 proposalId) external;
}
interface IWETH is IERC20 {
event Deposit(address indexed dst, uint256 wad);
event Withdrawal(address indexed src, uint256 wad);
function deposit() external payable;
function withdraw(uint256 wad) external;
}
interface ISTETH is IERC20 {
event Submitted(address indexed sender, uint256 amount, address referral);
// function() external payable;
function submit(address _referral) external payable returns (uint256);
}
interface IStETHWithdrawal {
event WithdrawalRequested(
uint256 indexed requestId,
address indexed requestor,
address indexed owner,
uint256 amountOfStETH,
uint256 amountOfShares
);
event WithdrawalsFinalized(
uint256 indexed from, uint256 indexed to, uint256 amountOfETHLocked, uint256 sharesToBurn, uint256 timestamp
);
event WithdrawalClaimed(
uint256 indexed requestId, address indexed owner, address indexed receiver, uint256 amountOfETH
);
struct WithdrawalRequestStatus {
/// @notice stETH token amount that was locked on withdrawal queue for this request
uint256 amountOfStETH;
/// @notice amount of stETH shares locked on withdrawal queue for this request
uint256 amountOfShares;
/// @notice address that can claim or transfer this request
address owner;
/// @notice timestamp of when the request was created, in seconds
uint256 timestamp;
/// @notice true, if request is finalized
bool isFinalized;
/// @notice true, if request is claimed. Request is claimable if (isFinalized && !isClaimed)
bool isClaimed;
}
function transferFrom(address _from, address _to, uint256 _requestId) external;
function ownerOf(uint256 _requestId) external returns (address);
function requestWithdrawals(uint256[] calldata _amounts, address _owner)
external
returns (uint256[] memory requestIds);
function getLastCheckpointIndex() external view returns (uint256);
function findCheckpointHints(uint256[] calldata _requestIds, uint256 _firstIndex, uint256 _lastIndex)
external
view
returns (uint256[] memory hintIds);
function claimWithdrawals(uint256[] calldata _requestIds, uint256[] calldata _hints) external;
function getWithdrawalStatus(uint256[] calldata _requestIds)
external
view
returns (WithdrawalRequestStatus[] memory statuses);
function getWithdrawalRequests(address _owner) external view returns (uint256[] memory requestsIds);
function getLastRequestId() external view returns (uint256);
}
interface IOracle {
function price(address asset) external view returns (uint256 price);
}
interface IHarvestable {
function collectRewards() external returns (address[] memory tokens, uint256[] memory rewards);
}
interface IMagpieRouter {
function swapWithMagpieSignature(bytes calldata) external payable returns (uint256 amountOut);
}
library DistributionTypes {
struct IncentivesProgramCreationInput {
string name;
address rewardToken;
uint104 emissionPerSecond;
uint40 distributionEnd;
}
}
library IDistributionManager {
struct AccruedRewards {
uint256 amount;
bytes32 programId;
address rewardToken;
}
}
interface SiloIncentivesControllerGaugeLike {
function claimRewards(address _to) external returns (IDistributionManager.AccruedRewards[] memory accruedRewards);
function createIncentivesProgram(DistributionTypes.IncentivesProgramCreationInput memory _incentivesProgramInput)
external;
function getAllProgramsNames() external view returns (string[] memory programsNames);
function getRewardsBalance(address _user, string memory _programName)
external
view
returns (uint256 unclaimedRewards);
function incentivesPrograms(bytes32)
external
view
returns (
uint256 index,
address rewardToken,
uint104 emissionPerSecond,
uint40 lastUpdateTimestamp,
uint40 distributionEnd
);
function owner() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/**
* @title Base contract that provides ownership control
* @author Origin Protocol Inc
*/
contract Ownable {
/// @notice The slot used to store the owner of the contract.
/// This is also used as the proxy admin.
/// keccak256(“eip1967.proxy.admin”) - 1 per EIP 1967
bytes32 internal constant OWNER_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
event AdminChanged(address previousAdmin, address newAdmin);
constructor() {
assert(OWNER_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
_setOwner(msg.sender);
}
/// @notice The contract owner and proxy admin.
function owner() external view returns (address) {
return _owner();
}
/// @notice Set the owner and proxy admin of the contract.
/// @param newOwner The address of the new owner.
function setOwner(address newOwner) external onlyOwner {
_setOwner(newOwner);
}
function _owner() internal view returns (address ownerOut) {
bytes32 position = OWNER_SLOT;
// solhint-disable-next-line no-inline-assembly
assembly {
ownerOut := sload(position)
}
}
function _setOwner(address newOwner) internal {
emit AdminChanged(_owner(), newOwner);
bytes32 position = OWNER_SLOT;
// solhint-disable-next-line no-inline-assembly
assembly {
sstore(position, newOwner)
}
}
function _onlyOwner() internal view {
require(msg.sender == _owner(), "ARM: Only owner can call this function.");
}
modifier onlyOwner() {
_onlyOwner();
_;
}
}{
"remappings": [
"contracts/=src/contracts/",
"script/=script/",
"test/=test/",
"utils/=src/contracts/utils/",
"forge-std/=dependencies/forge-std-1.9.3/src/",
"@openzeppelin/contracts/=dependencies/@openzeppelin-contracts-5.0.2/",
"@openzeppelin/contracts-upgradeable/=dependencies/@openzeppelin-contracts-upgradeable-5.0.2/",
"@solmate/=dependencies/solmate-6.7.0/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_liquidityAsset","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"actualBalance","type":"uint256"},{"internalType":"uint256","name":"minExpected","type":"uint256"}],"name":"BalanceMismatchAfterSwap","type":"error"},{"inputs":[],"name":"EmptyLiquidityAsset","type":"error"},{"inputs":[],"name":"EmptyMagpieRouter","type":"error"},{"inputs":[],"name":"EmptyRewardRecipient","type":"error"},{"inputs":[{"internalType":"uint256","name":"allowedSlippageBps","type":"uint256"}],"name":"InvalidAllowedSlippage","type":"error"},{"inputs":[],"name":"InvalidDecimals","type":"error"},{"inputs":[{"internalType":"address","name":"fromAsset","type":"address"}],"name":"InvalidFromAsset","type":"error"},{"inputs":[{"internalType":"uint256","name":"fromAssetAmount","type":"uint256"}],"name":"InvalidFromAssetAmount","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"enum SonicHarvester.SwapPlatform","name":"swapPlatform","type":"uint8"}],"name":"InvalidSwapPlatform","type":"error"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"InvalidSwapRecipient","type":"error"},{"inputs":[{"internalType":"address","name":"toAsset","type":"address"}],"name":"InvalidToAsset","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualBalance","type":"uint256"},{"internalType":"uint256","name":"minExpected","type":"uint256"}],"name":"SlippageError","type":"error"},{"inputs":[{"internalType":"address","name":"strategyAddress","type":"address"}],"name":"UnsupportedStrategy","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"allowedSlippageBps","type":"uint256"}],"name":"AllowedSlippageUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"router","type":"address"}],"name":"MagpieRouterUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"OperatorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"priceProvider","type":"address"}],"name":"PriceProviderUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"rewardRecipient","type":"address"}],"name":"RewardRecipientUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":true,"internalType":"address","name":"swappedInto","type":"address"},{"indexed":false,"internalType":"enum SonicHarvester.SwapPlatform","name":"swapPlatform","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"RewardTokenSwapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address[]","name":"strategy","type":"address[]"},{"indexed":false,"internalType":"address[][]","name":"rewardTokens","type":"address[][]"},{"indexed":false,"internalType":"uint256[][]","name":"amounts","type":"uint256[][]"}],"name":"RewardsCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"bool","name":"isSupported","type":"bool"}],"name":"SupportedStrategyUpdate","type":"event"},{"inputs":[],"name":"allowedSlippageBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_strategies","type":"address[]"}],"name":"collect","outputs":[{"internalType":"address[][]","name":"rewardTokens","type":"address[][]"},{"internalType":"uint256[][]","name":"amounts","type":"uint256[][]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_priceProvider","type":"address"},{"internalType":"uint256","name":"_allowedSlippageBps","type":"uint256"},{"internalType":"address","name":"_rewardRecipient","type":"address"},{"internalType":"address","name":"_magpieRouter","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidityAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"magpieRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceProvider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_allowedSlippageBps","type":"uint256"}],"name":"setAllowedSlippage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_router","type":"address"}],"name":"setMagpieRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOperator","type":"address"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_priceProvider","type":"address"}],"name":"setPriceProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardRecipient","type":"address"}],"name":"setRewardRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyAddress","type":"address"},{"internalType":"bool","name":"_isSupported","type":"bool"}],"name":"setSupportedStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"supportedStrategies","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum SonicHarvester.SwapPlatform","name":"swapPlatform","type":"uint8"},{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"uint256","name":"fromAssetAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[{"internalType":"uint256","name":"toAssetAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60a060405234801562000010575f80fd5b5060405162001946380380620019468339810160408190526200003391620001b7565b6200006060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6104620001e6565b5f8051602062001926833981519152146200007f576200007f6200020c565b6200008a336200014b565b6001600160a01b038116620000b257604051630641779360e11b815260040160405180910390fd5b806001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000ef573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000115919062000220565b60ff166012146200013957604051630692acc560e51b815260040160405180910390fd5b6001600160a01b031660805262000242565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f620001835f80516020620019268339815191525490565b604080516001600160a01b03928316815291841660208301520160405180910390a15f805160206200192683398151915255565b5f60208284031215620001c8575f80fd5b81516001600160a01b0381168114620001df575f80fd5b9392505050565b818103818111156200020657634e487b7160e01b5f52601160045260245ffd5b92915050565b634e487b7160e01b5f52600160045260245ffd5b5f6020828403121562000231575f80fd5b815160ff81168114620001df575f80fd5b6080516116af620002775f395f81816101890152818161056001528181610635015281816106d70152610e5a01526116af5ff3fe608060405234801561000f575f80fd5b5060043610610111575f3560e01c806365f6fa941161009e578063b3ab15fb1161006e578063b3ab15fb14610245578063b888879e14610258578063e4adb4031461026b578063e521136f1461027e578063ee3be5f514610291575f80fd5b806365f6fa94146101f6578063664fd3b2146102095780638da5cb5b1461021c578063a4520aee14610224575f80fd5b8063209b2bca116100e4578063209b2bca146101845780632365faea146101ab578063358394d8146101be578063372aa224146101d1578063570ca735146101e4575f80fd5b80630cf157ba146101155780631297fc4f1461013157806313af40351461015c57806317f3334014610171575b5f80fd5b61011e60345481565b6040519081526020015b60405180910390f35b603654610144906001600160a01b031681565b6040516001600160a01b039091168152602001610128565b61016f61016a366004611044565b6102c3565b005b603554610144906001600160a01b031681565b6101447f000000000000000000000000000000000000000000000000000000000000000081565b61016f6101b9366004611066565b6102d7565b61016f6101cc36600461107d565b6102e8565b61016f6101df366004611044565b61041c565b5f54610144906001600160a01b031681565b61016f6102043660046110da565b61042d565b61011e610217366004611111565b610497565b61014461084a565b6102376102323660046111ab565b610865565b604051610128929190611295565b61016f610253366004611044565b610aca565b603354610144906001600160a01b031681565b61016f610279366004611044565b610adb565b61016f61028c366004611044565b610aec565b6102b361029f366004611044565b60326020525f908152604090205460ff1681565b6040519015158152602001610128565b6102cb610afd565b6102d481610b7e565b50565b6102df610afd565b6102d481610be7565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f8115801561032d5750825b90505f8267ffffffffffffffff1660011480156103495750303b155b905081158015610357575080155b156103755760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561039f57845460ff60401b1916600160401b1785555b6103a7610afd565b6103b089610c49565b6103b988610be7565b6103c287610c97565b6103cb86610d0c565b831561041157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b610424610afd565b6102d481610c49565b610435610afd565b6001600160a01b0382165f81815260326020908152604091829020805460ff19168515159081179091558251938452908301527f013ed61add17cbfcbbd95bf8543da67c89658c5477d3f3199a1a2d58ecf1913f910160405180910390a15050565b5f80546001600160a01b03163314806104cf57505f8051602061165a833981519152546001600160a01b0316336001600160a01b0316145b61053c5760405162461bcd60e51b815260206004820152603360248201527f41524d3a204f6e6c79206f70657261746f72206f72206f776e65722063616e2060448201527231b0b636103a3434b990333ab731ba34b7b71760691b60648201526084015b60405180910390fd5b6035546040516370a0823160e01b81526001600160a01b0391821660048201525f917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156105a5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105c9919061132f565b905061060c87878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610d8192505050565b6035546040516370a0823160e01b81526001600160a01b0391821660048201529193505f9183917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa15801561067a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061069e919061132f565b6106a8919061135a565b9050828110156106d55760405163315d50df60e11b81526004810182905260248101849052604401610533565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b03167fa861903141bc68b536d5048a576afcc645630e1b18a4296ef34cbd4d1407f7098a898760405161073c939291906113a7565b60405180910390a36033546001600160a01b031661075b575050610841565b6033546040516315d5220f60e31b81526001600160a01b0389811660048301525f92169063aea9107890602401602060405180830381865afa1580156107a3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107c7919061132f565b90505f670de0b6b3a7640000612710836034546127106107e7919061135a565b6107f1908c6113c6565b6107fb91906113c6565b61080591906113dd565b61080f91906113dd565b90508085101561083c576040516302d96fff60e41b81526004810186905260248101829052604401610533565b505050505b95945050505050565b5f6108605f8051602061165a8339815191525490565b905090565b6060808267ffffffffffffffff811115610881576108816113fc565b6040519080825280602002602001820160405280156108b457816020015b606081526020019060019003908161089f5790505b5091508267ffffffffffffffff8111156108d0576108d06113fc565b60405190808252806020026020018201604052801561090357816020015b60608152602001906001900390816108ee5790505b5090505f5b83811015610a715760325f86868481811061092557610925611410565b905060200201602081019061093a9190611044565b6001600160a01b0316815260208101919091526040015f205460ff166109a65784848281811061096c5761096c611410565b90506020020160208101906109819190611044565b604051630211444960e11b81526001600160a01b039091166004820152602401610533565b8484828181106109b8576109b8611410565b90506020020160208101906109cd9190611044565b6001600160a01b03166370bb45b36040518163ffffffff1660e01b81526004015f604051808303815f875af1158015610a08573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610a2f91908101906114e4565b848381518110610a4157610a41611410565b60200260200101848481518110610a5a57610a5a611410565b602090810291909101019190915252600101610908565b508383604051610a829291906115a3565b60405180910390207f821598254cf2ff9260ed0cadfa73031860381ece8b317190057aa6383b7398078383604051610abb929190611295565b60405180910390a29250929050565b610ad2610afd565b6102d481610fe3565b610ae3610afd565b6102d481610d0c565b610af4610afd565b6102d481610c97565b5f8051602061165a833981519152546001600160a01b0316336001600160a01b031614610b7c5760405162461bcd60e51b815260206004820152602760248201527f41524d3a204f6e6c79206f776e65722063616e2063616c6c20746869732066756044820152663731ba34b7b71760c91b6064820152608401610533565b565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610bb45f8051602061165a8339815191525490565b604080516001600160a01b03928316815291841660208301520160405180910390a15f8051602061165a83398151915255565b6103e8811115610c0d57604051630fbdd3e560e41b815260048101829052602401610533565b60348190556040518181527f0a5ebec94541f30d73911805a35146177558dad0cb26dde7918aa99b028bf5e1906020015b60405180910390a150565b603380546001600160a01b0319166001600160a01b0383169081179091556040519081527fb266add5f3044b17d27db796af992cecbe413921b4e8aaaee03c719e16b9806a90602001610c3e565b6001600160a01b038116610cbe57604051630c45e03360e01b815260040160405180910390fd5b603580546001600160a01b0319166001600160a01b0383169081179091556040519081527fab0699d690a6fcb85f70e4d28ee4c6114c3d90c12c579ca5abe089b8ff6ff1bf90602001610c3e565b6001600160a01b038116610d3357604051632444471360e01b815260040160405180910390fd5b603680546001600160a01b0319166001600160a01b0383169081179091556040519081527f20cfd10985b2bec461593c76dd1111b15fed562c5572bb7e6500112d12ad706590602001610c3e565b5f80858015610d9257610d92611373565b03610fc05760248201516038830151604c8401516069850151606a86015160f01c80870160231990810151603554606097881c9796871c969590951c9460f89490941c939190920191831c906001600160a01b03168614610e1157604051631a7c14f360e01b81526001600160a01b0387166004820152602401610533565b846001600160a01b03168a6001600160a01b031614610e4e5760405163187c383360e31b81526001600160a01b0386166004820152602401610533565b836001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614610eab57604051631b90a34360e31b81526001600160a01b0385166004820152602401610533565b808914610ece5760405163145113a160e21b815260048101829052602401610533565b60365460405163095ea7b360e01b81526001600160a01b039182166004820152602481018b9052908b169063095ea7b3906044016020604051808303815f875af1158015610f1e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f4291906115e4565b506036546040516373fc445760e01b81526001600160a01b03909116906373fc445790610f73908b906004016115ff565b6020604051808303815f875af1158015610f8f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fb3919061132f565b9650505050505050610fdb565b846040516336cb1d2160e01b8152600401610533919061164b565b949350505050565b5f80546001600160a01b0319166001600160a01b0383169081179091556040519081527f4721129e0e676ed6a92909bb24e853ccdd63ad72280cc2e974e38e480e0e6e5490602001610c3e565b6001600160a01b03811681146102d4575f80fd5b5f60208284031215611054575f80fd5b813561105f81611030565b9392505050565b5f60208284031215611076575f80fd5b5035919050565b5f805f8060808587031215611090575f80fd5b843561109b81611030565b93506020850135925060408501356110b281611030565b915060608501356110c281611030565b939692955090935050565b80151581146102d4575f80fd5b5f80604083850312156110eb575f80fd5b82356110f681611030565b91506020830135611106816110cd565b809150509250929050565b5f805f805f60808688031215611125575f80fd5b853560018110611133575f80fd5b9450602086013561114381611030565b935060408601359250606086013567ffffffffffffffff80821115611166575f80fd5b818801915088601f830112611179575f80fd5b813581811115611187575f80fd5b896020828501011115611198575f80fd5b9699959850939650602001949392505050565b5f80602083850312156111bc575f80fd5b823567ffffffffffffffff808211156111d3575f80fd5b818501915085601f8301126111e6575f80fd5b8135818111156111f4575f80fd5b8660208260051b8501011115611208575f80fd5b60209290920196919550909350505050565b5f82825180855260208086019550808260051b8401018186015f5b8481101561128857858303601f190189528151805180855290850190858501905f5b8181101561127357835183529287019291870191600101611257565b50509985019993505090830190600101611235565b5090979650505050505050565b5f604082016040835280855180835260608501915060608160051b860101925060208088015f5b8381101561131957878603605f190185528151805180885290840190848801905f5b818110156113035783516001600160a01b0316835292860192918601916001016112de565b50909750505093820193908201906001016112bc565b505085840381870152505050610841818561121a565b5f6020828403121561133f575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561136d5761136d611346565b92915050565b634e487b7160e01b5f52602160045260245ffd5b600181106113a357634e487b7160e01b5f52602160045260245ffd5b9052565b606081016113b58286611387565b602082019390935260400152919050565b808202811582820484141761136d5761136d611346565b5f826113f757634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561144d5761144d6113fc565b604052919050565b5f67ffffffffffffffff82111561146e5761146e6113fc565b5060051b60200190565b5f82601f830112611487575f80fd5b8151602061149c61149783611455565b611424565b8083825260208201915060208460051b8701019350868411156114bd575f80fd5b602086015b848110156114d957805183529183019183016114c2565b509695505050505050565b5f80604083850312156114f5575f80fd5b825167ffffffffffffffff8082111561150c575f80fd5b818501915085601f83011261151f575f80fd5b8151602061152f61149783611455565b82815260059290921b8401810191818101908984111561154d575f80fd5b948201945b8386101561157457855161156581611030565b82529482019490820190611552565b9188015191965090935050508082111561158c575f80fd5b5061159985828601611478565b9150509250929050565b5f8184825b858110156115d95781356115bb81611030565b6001600160a01b0316835260209283019291909101906001016115a8565b509095945050505050565b5f602082840312156115f4575f80fd5b815161105f816110cd565b5f602080835283518060208501525f5b8181101561162b5785810183015185820160400152820161160f565b505f604082860101526040601f19601f8301168501019250505092915050565b6020810161136d828461138756feb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a264697066735822122083069a21e65175b2c4778b95d976ad8f1ff4b23b59e9b706606369cc3e1c532d64736f6c63430008170033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad38
Deployed Bytecode
0x608060405234801561000f575f80fd5b5060043610610111575f3560e01c806365f6fa941161009e578063b3ab15fb1161006e578063b3ab15fb14610245578063b888879e14610258578063e4adb4031461026b578063e521136f1461027e578063ee3be5f514610291575f80fd5b806365f6fa94146101f6578063664fd3b2146102095780638da5cb5b1461021c578063a4520aee14610224575f80fd5b8063209b2bca116100e4578063209b2bca146101845780632365faea146101ab578063358394d8146101be578063372aa224146101d1578063570ca735146101e4575f80fd5b80630cf157ba146101155780631297fc4f1461013157806313af40351461015c57806317f3334014610171575b5f80fd5b61011e60345481565b6040519081526020015b60405180910390f35b603654610144906001600160a01b031681565b6040516001600160a01b039091168152602001610128565b61016f61016a366004611044565b6102c3565b005b603554610144906001600160a01b031681565b6101447f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad3881565b61016f6101b9366004611066565b6102d7565b61016f6101cc36600461107d565b6102e8565b61016f6101df366004611044565b61041c565b5f54610144906001600160a01b031681565b61016f6102043660046110da565b61042d565b61011e610217366004611111565b610497565b61014461084a565b6102376102323660046111ab565b610865565b604051610128929190611295565b61016f610253366004611044565b610aca565b603354610144906001600160a01b031681565b61016f610279366004611044565b610adb565b61016f61028c366004611044565b610aec565b6102b361029f366004611044565b60326020525f908152604090205460ff1681565b6040519015158152602001610128565b6102cb610afd565b6102d481610b7e565b50565b6102df610afd565b6102d481610be7565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f8115801561032d5750825b90505f8267ffffffffffffffff1660011480156103495750303b155b905081158015610357575080155b156103755760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561039f57845460ff60401b1916600160401b1785555b6103a7610afd565b6103b089610c49565b6103b988610be7565b6103c287610c97565b6103cb86610d0c565b831561041157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b610424610afd565b6102d481610c49565b610435610afd565b6001600160a01b0382165f81815260326020908152604091829020805460ff19168515159081179091558251938452908301527f013ed61add17cbfcbbd95bf8543da67c89658c5477d3f3199a1a2d58ecf1913f910160405180910390a15050565b5f80546001600160a01b03163314806104cf57505f8051602061165a833981519152546001600160a01b0316336001600160a01b0316145b61053c5760405162461bcd60e51b815260206004820152603360248201527f41524d3a204f6e6c79206f70657261746f72206f72206f776e65722063616e2060448201527231b0b636103a3434b990333ab731ba34b7b71760691b60648201526084015b60405180910390fd5b6035546040516370a0823160e01b81526001600160a01b0391821660048201525f917f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad3816906370a0823190602401602060405180830381865afa1580156105a5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105c9919061132f565b905061060c87878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610d8192505050565b6035546040516370a0823160e01b81526001600160a01b0391821660048201529193505f9183917f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad3816906370a0823190602401602060405180830381865afa15801561067a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061069e919061132f565b6106a8919061135a565b9050828110156106d55760405163315d50df60e11b81526004810182905260248101849052604401610533565b7f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad386001600160a01b0316876001600160a01b03167fa861903141bc68b536d5048a576afcc645630e1b18a4296ef34cbd4d1407f7098a898760405161073c939291906113a7565b60405180910390a36033546001600160a01b031661075b575050610841565b6033546040516315d5220f60e31b81526001600160a01b0389811660048301525f92169063aea9107890602401602060405180830381865afa1580156107a3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107c7919061132f565b90505f670de0b6b3a7640000612710836034546127106107e7919061135a565b6107f1908c6113c6565b6107fb91906113c6565b61080591906113dd565b61080f91906113dd565b90508085101561083c576040516302d96fff60e41b81526004810186905260248101829052604401610533565b505050505b95945050505050565b5f6108605f8051602061165a8339815191525490565b905090565b6060808267ffffffffffffffff811115610881576108816113fc565b6040519080825280602002602001820160405280156108b457816020015b606081526020019060019003908161089f5790505b5091508267ffffffffffffffff8111156108d0576108d06113fc565b60405190808252806020026020018201604052801561090357816020015b60608152602001906001900390816108ee5790505b5090505f5b83811015610a715760325f86868481811061092557610925611410565b905060200201602081019061093a9190611044565b6001600160a01b0316815260208101919091526040015f205460ff166109a65784848281811061096c5761096c611410565b90506020020160208101906109819190611044565b604051630211444960e11b81526001600160a01b039091166004820152602401610533565b8484828181106109b8576109b8611410565b90506020020160208101906109cd9190611044565b6001600160a01b03166370bb45b36040518163ffffffff1660e01b81526004015f604051808303815f875af1158015610a08573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610a2f91908101906114e4565b848381518110610a4157610a41611410565b60200260200101848481518110610a5a57610a5a611410565b602090810291909101019190915252600101610908565b508383604051610a829291906115a3565b60405180910390207f821598254cf2ff9260ed0cadfa73031860381ece8b317190057aa6383b7398078383604051610abb929190611295565b60405180910390a29250929050565b610ad2610afd565b6102d481610fe3565b610ae3610afd565b6102d481610d0c565b610af4610afd565b6102d481610c97565b5f8051602061165a833981519152546001600160a01b0316336001600160a01b031614610b7c5760405162461bcd60e51b815260206004820152602760248201527f41524d3a204f6e6c79206f776e65722063616e2063616c6c20746869732066756044820152663731ba34b7b71760c91b6064820152608401610533565b565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610bb45f8051602061165a8339815191525490565b604080516001600160a01b03928316815291841660208301520160405180910390a15f8051602061165a83398151915255565b6103e8811115610c0d57604051630fbdd3e560e41b815260048101829052602401610533565b60348190556040518181527f0a5ebec94541f30d73911805a35146177558dad0cb26dde7918aa99b028bf5e1906020015b60405180910390a150565b603380546001600160a01b0319166001600160a01b0383169081179091556040519081527fb266add5f3044b17d27db796af992cecbe413921b4e8aaaee03c719e16b9806a90602001610c3e565b6001600160a01b038116610cbe57604051630c45e03360e01b815260040160405180910390fd5b603580546001600160a01b0319166001600160a01b0383169081179091556040519081527fab0699d690a6fcb85f70e4d28ee4c6114c3d90c12c579ca5abe089b8ff6ff1bf90602001610c3e565b6001600160a01b038116610d3357604051632444471360e01b815260040160405180910390fd5b603680546001600160a01b0319166001600160a01b0383169081179091556040519081527f20cfd10985b2bec461593c76dd1111b15fed562c5572bb7e6500112d12ad706590602001610c3e565b5f80858015610d9257610d92611373565b03610fc05760248201516038830151604c8401516069850151606a86015160f01c80870160231990810151603554606097881c9796871c969590951c9460f89490941c939190920191831c906001600160a01b03168614610e1157604051631a7c14f360e01b81526001600160a01b0387166004820152602401610533565b846001600160a01b03168a6001600160a01b031614610e4e5760405163187c383360e31b81526001600160a01b0386166004820152602401610533565b836001600160a01b03167f000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad386001600160a01b031614610eab57604051631b90a34360e31b81526001600160a01b0385166004820152602401610533565b808914610ece5760405163145113a160e21b815260048101829052602401610533565b60365460405163095ea7b360e01b81526001600160a01b039182166004820152602481018b9052908b169063095ea7b3906044016020604051808303815f875af1158015610f1e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f4291906115e4565b506036546040516373fc445760e01b81526001600160a01b03909116906373fc445790610f73908b906004016115ff565b6020604051808303815f875af1158015610f8f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fb3919061132f565b9650505050505050610fdb565b846040516336cb1d2160e01b8152600401610533919061164b565b949350505050565b5f80546001600160a01b0319166001600160a01b0383169081179091556040519081527f4721129e0e676ed6a92909bb24e853ccdd63ad72280cc2e974e38e480e0e6e5490602001610c3e565b6001600160a01b03811681146102d4575f80fd5b5f60208284031215611054575f80fd5b813561105f81611030565b9392505050565b5f60208284031215611076575f80fd5b5035919050565b5f805f8060808587031215611090575f80fd5b843561109b81611030565b93506020850135925060408501356110b281611030565b915060608501356110c281611030565b939692955090935050565b80151581146102d4575f80fd5b5f80604083850312156110eb575f80fd5b82356110f681611030565b91506020830135611106816110cd565b809150509250929050565b5f805f805f60808688031215611125575f80fd5b853560018110611133575f80fd5b9450602086013561114381611030565b935060408601359250606086013567ffffffffffffffff80821115611166575f80fd5b818801915088601f830112611179575f80fd5b813581811115611187575f80fd5b896020828501011115611198575f80fd5b9699959850939650602001949392505050565b5f80602083850312156111bc575f80fd5b823567ffffffffffffffff808211156111d3575f80fd5b818501915085601f8301126111e6575f80fd5b8135818111156111f4575f80fd5b8660208260051b8501011115611208575f80fd5b60209290920196919550909350505050565b5f82825180855260208086019550808260051b8401018186015f5b8481101561128857858303601f190189528151805180855290850190858501905f5b8181101561127357835183529287019291870191600101611257565b50509985019993505090830190600101611235565b5090979650505050505050565b5f604082016040835280855180835260608501915060608160051b860101925060208088015f5b8381101561131957878603605f190185528151805180885290840190848801905f5b818110156113035783516001600160a01b0316835292860192918601916001016112de565b50909750505093820193908201906001016112bc565b505085840381870152505050610841818561121a565b5f6020828403121561133f575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561136d5761136d611346565b92915050565b634e487b7160e01b5f52602160045260245ffd5b600181106113a357634e487b7160e01b5f52602160045260245ffd5b9052565b606081016113b58286611387565b602082019390935260400152919050565b808202811582820484141761136d5761136d611346565b5f826113f757634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561144d5761144d6113fc565b604052919050565b5f67ffffffffffffffff82111561146e5761146e6113fc565b5060051b60200190565b5f82601f830112611487575f80fd5b8151602061149c61149783611455565b611424565b8083825260208201915060208460051b8701019350868411156114bd575f80fd5b602086015b848110156114d957805183529183019183016114c2565b509695505050505050565b5f80604083850312156114f5575f80fd5b825167ffffffffffffffff8082111561150c575f80fd5b818501915085601f83011261151f575f80fd5b8151602061152f61149783611455565b82815260059290921b8401810191818101908984111561154d575f80fd5b948201945b8386101561157457855161156581611030565b82529482019490820190611552565b9188015191965090935050508082111561158c575f80fd5b5061159985828601611478565b9150509250929050565b5f8184825b858110156115d95781356115bb81611030565b6001600160a01b0316835260209283019291909101906001016115a8565b509095945050505050565b5f602082840312156115f4575f80fd5b815161105f816110cd565b5f602080835283518060208501525f5b8181101561162b5785810183015185820160400152820161160f565b505f604082860101526040601f19601f8301168501019250505092915050565b6020810161136d828461138756feb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103a264697066735822122083069a21e65175b2c4778b95d976ad8f1ff4b23b59e9b706606369cc3e1c532d64736f6c63430008170033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad38
-----Decoded View---------------
Arg [0] : _liquidityAsset (address): 0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad38
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.