Source Code
Overview
S Balance
More Info
ContractCreator
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
SaraDEX
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "./SaraLiquidityManager.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; contract SaraDEX is Ownable { using SafeERC20 for IERC20; address public sonicToken; // Address of Sonic's native token (S) SaraLiquidityManager public liquidityManager; uint256 public swapFee = 30; // 0.3% fee (divided by 10,000 for precision) // Add anti-bot protection mapping(address => uint256) public lastSwapTimestamp; uint256 public constant MIN_TIME_BETWEEN_SWAPS = 1 minutes; uint256 public constant MAX_SWAP_AMOUNT_PERCENT = 5; // 5% of reserves // Add fee storage tracking mapping(address => uint256) public storedFees; uint256 public constant MAX_SINGLE_SWAP = 1000 * 1e18; // 1000 tokens per swap // Change from constant to regular state variable uint256 public AUTO_REDEPLOY_THRESHOLD = 1000 * 1e18; // 1000 tokens initial value // Change from constant to immutable/variable uint256 public immutable DEFAULT_MAX_SLIPPAGE = 500; // 5% uint256 public ABSOLUTE_MAX_SLIPPAGE = 1000; // 10% initial cap uint256 public constant MAXIMUM_ALLOWED_SLIPPAGE = 2000; // 20% absolute maximum // Add error messages as constants string private constant ERR_SAME_VALUE = "New slippage same as current"; string private constant ERR_LOW_SLIPPAGE = "Cannot be lower than default"; string private constant ERR_HIGH_SLIPPAGE = "Cannot exceed 20%"; event SwapExecuted( address indexed user, address indexed creatorToken, uint256 amountIn, uint256 amountOut, uint256 fee ); event FeesWithdrawn(address indexed token, uint256 amount); // Add event for slippage updates event MaxSlippageUpdated( uint256 oldSlippage, uint256 newSlippage, address indexed updater ); // Add new events at the top event SlippageUpdated( uint256 oldSlippage, uint256 newSlippage, address indexed updatedBy ); event SwapFeesUpdated( uint256 oldFee, uint256 newFee, address indexed updatedBy ); event LiquidityManagerUpdated( address indexed oldManager, address indexed newManager, address indexed updatedBy ); event AutoRedeployThresholdUpdated( uint256 oldThreshold, uint256 newThreshold, address indexed updatedBy ); // Add state variables at the top bool public paused; uint256 public lastPauseTimestamp; // Add pause-related events event ContractPaused( address indexed by, uint256 timestamp ); event ContractUnpaused( address indexed by, uint256 timestamp ); event EmergencyWithdraw( address indexed token, uint256 amount, address indexed to, uint256 timestamp ); // Add both pause-related modifiers modifier whenNotPaused() { require(!paused, "Contract is paused"); _; } modifier whenPaused() { require(paused, "Contract not paused"); _; } // Add at the top with other constants uint256 public constant MIN_LIQUIDITY = 1000 * 1e18; // 1000 tokens minimum liquidity constructor( address _sonicToken, address payable _liquidityManager ) Ownable(msg.sender) { // Pass msg.sender to Ownable constructor sonicToken = _sonicToken; liquidityManager = SaraLiquidityManager(_liquidityManager); } function swapCreatorTokenForS( address creatorToken, uint256 amountIn, uint256 minAmountOut, uint256 maxSlippage ) external payable whenNotPaused { // Add anti-bot check require(block.timestamp >= lastSwapTimestamp[msg.sender] + MIN_TIME_BETWEEN_SWAPS, "Too many swaps"); // Get reserves (uint256 reserveCreator, uint256 reserveS) = liquidityManager.getReserves(creatorToken); // Add liquidity checks require(amountIn <= reserveCreator, "Insufficient liquidity in pool"); require(reserveS > 0, "No S token liquidity"); // Add large swap protection require(amountIn <= reserveCreator * MAX_SWAP_AMOUNT_PERCENT / 100, "Swap too large"); require(amountIn > 0, "Invalid amount"); require(IERC20(creatorToken).balanceOf(msg.sender) >= amountIn, "Insufficient balance"); // Calculate output amount uint256 amountOut = getAmountOut(amountIn, reserveCreator, reserveS); require(amountOut >= minAmountOut, "Slippage too high"); // Calculate fee uint256 fee = (amountOut * swapFee) / 10000; uint256 amountOutAfterFee = amountOut - fee; // Split large swaps if (amountIn > MAX_SINGLE_SWAP) { uint256 remainingAmount = amountIn; uint256 totalReceived = 0; while (remainingAmount > 0) { uint256 currentSwapAmount = remainingAmount > MAX_SINGLE_SWAP ? MAX_SINGLE_SWAP : remainingAmount; // Execute partial swap uint256 received = _executeSwap( creatorToken, currentSwapAmount, maxSlippage ); totalReceived += received; remainingAmount -= currentSwapAmount; } require(totalReceived >= minAmountOut, "Slippage too high"); return; } // Transfer creator tokens from user IERC20(creatorToken).safeTransferFrom(msg.sender, address(this), amountIn); // Update state storedFees[sonicToken] += fee; // Check for auto-redeployment if (storedFees[sonicToken] >= AUTO_REDEPLOY_THRESHOLD) { liquidityManager.autoRedeployFees(sonicToken); } // Transfer S tokens to user (bool success,) = msg.sender.call{value: amountOutAfterFee}(""); require(success, "S token transfer failed"); // Update timestamp and emit event lastSwapTimestamp[msg.sender] = block.timestamp; emit SwapExecuted(msg.sender, creatorToken, amountIn, amountOutAfterFee, fee); } function _executeSwap( address creatorToken, uint256 amountIn, uint256 maxSlippage ) internal returns (uint256) { // Validate slippage first to save gas require(maxSlippage <= ABSOLUTE_MAX_SLIPPAGE, "Slippage too high"); uint256 slippageUsed = maxSlippage > 0 ? maxSlippage : DEFAULT_MAX_SLIPPAGE; require(slippageUsed >= DEFAULT_MAX_SLIPPAGE, "Invalid slippage"); // Check approvals before any calculations require( IERC20(creatorToken).allowance(msg.sender, address(this)) >= amountIn, "Insufficient creator token allowance" ); // Get reserves and check liquidity (uint256 reserveCreator, uint256 reserveS) = liquidityManager.getReserves(creatorToken); require(amountIn <= reserveCreator, "Insufficient liquidity in pool"); require(reserveS > amountIn, "Insufficient S token liquidity"); // Calculate output amount uint256 amountOut = getAmountOut(amountIn, reserveCreator, reserveS); uint256 minAcceptableOutput = amountOut - ((amountOut * slippageUsed) / 10000); // Apply swap fee uint256 fee = (amountOut * swapFee) / 10000; uint256 amountOutAfterFee = amountOut - fee; // Check slippage tolerance require(amountOutAfterFee >= minAcceptableOutput, "Slippage too high"); // Check native S token balance require(address(this).balance >= amountOutAfterFee, "Insufficient S balance"); // Execute transfers IERC20(creatorToken).safeTransferFrom(msg.sender, address(this), amountIn); // Transfer native S (bool success,) = msg.sender.call{value: amountOutAfterFee}(""); require(success, "S token transfer failed"); // Update state storedFees[sonicToken] += fee; // Check for auto-redeployment if (storedFees[sonicToken] >= AUTO_REDEPLOY_THRESHOLD) { liquidityManager.autoRedeployFees(sonicToken); } // Update timestamp and emit event lastSwapTimestamp[msg.sender] = block.timestamp; emit SwapExecuted(msg.sender, creatorToken, amountIn, amountOutAfterFee, fee); return amountOutAfterFee; } /** * @dev Calculates output amount based on constant product formula * @param amountIn Amount of input tokens * @param reserveIn Reserve of input token * @param reserveOut Reserve of output token * @return Amount of output tokens */ function getAmountOut( uint256 amountIn, uint256 reserveIn, uint256 reserveOut ) public pure returns (uint256) { require(amountIn > 0, "Invalid input amount"); require(reserveIn > 0 && reserveOut > 0, "Invalid reserves"); // Use more precise fee calculation (0.25% fee = 9975/10000) uint256 amountInWithFee = amountIn * 9975; // Calculate with higher precision uint256 numerator = amountInWithFee * reserveOut; uint256 denominator = (reserveIn * 10000) + amountInWithFee; // Prevent division by zero (though require above should catch this) require(denominator > 0, "Invalid calculation"); return numerator / denominator; } function withdrawStoredFees(address token, uint256 amount) external onlyOwner { require(storedFees[token] >= amount, "Insufficient stored fees"); storedFees[token] -= amount; if (isNativeS(token)) { (bool success,) = msg.sender.call{value: amount}(""); require(success, "S token transfer failed"); } else { IERC20(token).safeTransfer(msg.sender, amount); } emit FeesWithdrawn(token, amount); } /** * @dev Updates the maximum allowed slippage with optimizations * @param newMaxSlippage New maximum slippage in basis points (e.g., 1000 = 10%) */ function updateMaxSlippage(uint256 newMaxSlippage) external onlyOwner { // Check if value is actually changing require(newMaxSlippage != ABSOLUTE_MAX_SLIPPAGE, ERR_SAME_VALUE); // Validate bounds require(newMaxSlippage >= DEFAULT_MAX_SLIPPAGE, ERR_LOW_SLIPPAGE); require(newMaxSlippage <= MAXIMUM_ALLOWED_SLIPPAGE, ERR_HIGH_SLIPPAGE); // Store old value for event uint256 oldSlippage = ABSOLUTE_MAX_SLIPPAGE; // Update slippage ABSOLUTE_MAX_SLIPPAGE = newMaxSlippage; // Emit event with old and new values emit SlippageUpdated( oldSlippage, newMaxSlippage, msg.sender ); } /** * @dev View function to validate potential slippage value * @param slippage Slippage value to validate * @return bool Valid or not * @return string Memory reason if invalid */ function validateSlippage( uint256 slippage ) external view returns (bool, string memory) { if (slippage == ABSOLUTE_MAX_SLIPPAGE) return (false, ERR_SAME_VALUE); if (slippage < DEFAULT_MAX_SLIPPAGE) return (false, ERR_LOW_SLIPPAGE); if (slippage > MAXIMUM_ALLOWED_SLIPPAGE) return (false, ERR_HIGH_SLIPPAGE); return (true, ""); } /** * @dev View function to get current slippage settings */ function getSlippageSettings() external view returns ( uint256 defaultSlippage, uint256 currentMaxSlippage, uint256 absoluteMaximum ) { return ( DEFAULT_MAX_SLIPPAGE, ABSOLUTE_MAX_SLIPPAGE, MAXIMUM_ALLOWED_SLIPPAGE ); } // Add receive function to accept native S tokens receive() external payable {} // Add helper function function isNativeS(address token) internal pure returns (bool) { return token == address(0) || token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; } // Add function to update swap fee function updateSwapFee(uint256 newFee) external onlyOwner { require(newFee > 0 && newFee <= 500, "Invalid fee"); // Max 5% uint256 oldFee = swapFee; swapFee = newFee; emit SwapFeesUpdated(oldFee, newFee, msg.sender); } // Add function to update liquidity manager function updateLiquidityManager(address payable newManager) external onlyOwner { require(newManager != address(0), "Invalid manager address"); address oldManager = address(liquidityManager); liquidityManager = SaraLiquidityManager(newManager); emit LiquidityManagerUpdated(oldManager, newManager, msg.sender); } // Add function to update auto redeploy threshold function updateAutoRedeployThreshold(uint256 newThreshold) external onlyOwner { require(newThreshold >= MIN_LIQUIDITY, "Below minimum liquidity"); uint256 oldThreshold = AUTO_REDEPLOY_THRESHOLD; AUTO_REDEPLOY_THRESHOLD = newThreshold; emit AutoRedeployThresholdUpdated(oldThreshold, newThreshold, msg.sender); } // Add view function to get current fee rate function getFeeRate() external pure returns ( uint256 feeNumerator, uint256 feeDenominator, string memory description ) { return ( 25, // 0.25% 10000, "0.25% fee (25/10000)" ); } // Add pause functions function pause() external onlyOwner { require(!paused, "Already paused"); paused = true; lastPauseTimestamp = block.timestamp; emit ContractPaused(msg.sender, block.timestamp); } function unpause() external onlyOwner { require(paused, "Not paused"); paused = false; emit ContractUnpaused(msg.sender, block.timestamp); } // Add emergency withdraw function function emergencyWithdraw( address token, uint256 amount, address payable to ) external onlyOwner whenPaused { require(to != address(0), "Invalid recipient"); if (isNativeS(token)) { require(address(this).balance >= amount, "Insufficient S balance"); (bool success,) = to.call{value: amount}(""); require(success, "S token transfer failed"); } else { require( IERC20(token).balanceOf(address(this)) >= amount, "Insufficient token balance" ); IERC20(token).safeTransfer(to, amount); } emit EmergencyWithdraw(token, amount, to, block.timestamp); } // Add view function to get pause status function getPauseStatus() external view returns ( bool isPaused, uint256 pauseDuration, uint256 lastPause ) { return ( paused, paused ? block.timestamp - lastPauseTimestamp : 0, lastPauseTimestamp ); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol) pragma solidity ^0.8.20; import {IAccessControl} from "./IAccessControl.sol"; import {Context} from "../utils/Context.sol"; import {ERC165} from "../utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address account => bool) hasRole; bytes32 adminRole; } mapping(bytes32 role => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with an {AccessControlUnauthorizedAccount} error including the required role. */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual returns (bool) { return _roles[role].hasRole[account]; } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()` * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier. */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account` * is missing `role`. */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert AccessControlUnauthorizedAccount(account, role); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address callerConfirmation) public virtual { if (callerConfirmation != _msgSender()) { revert AccessControlBadConfirmation(); } _revokeRole(role, callerConfirmation); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual returns (bool) { if (!hasRole(role, account)) { _roles[role].hasRole[account] = true; emit RoleGranted(role, account, _msgSender()); return true; } else { return false; } } /** * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual returns (bool) { if (hasRole(role, account)) { _roles[role].hasRole[account] = false; emit RoleRevoked(role, account, _msgSender()); return true; } else { return false; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol) pragma solidity ^0.8.20; /** * @dev External interface of AccessControl declared to support ERC-165 detection. */ interface IAccessControl { /** * @dev The `account` is missing a role. */ error AccessControlUnauthorizedAccount(address account, bytes32 neededRole); /** * @dev The caller of a function is not the expected one. * * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}. */ error AccessControlBadConfirmation(); /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role). * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. */ function renounceRole(bytes32 role, address callerConfirmation) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is set to the address provided by the deployer. This can * later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ constructor(address initialOwner) { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC165} from "./IERC165.sol"; /** * @title IERC1363 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363]. * * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction. */ interface IERC1363 is IERC20, IERC165 { /* * Note: the ERC-165 identifier for this interface is 0xb0202a11. * 0xb0202a11 === * bytes4(keccak256('transferAndCall(address,uint256)')) ^ * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^ * bytes4(keccak256('approveAndCall(address,uint256)')) ^ * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) */ /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @param data Additional data with no specified format, sent in call to `spender`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ 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.2.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC1363} from "../../../interfaces/IERC1363.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC-20 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 { /** * @dev An operation with an ERC-20 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. * * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client" * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior. */ 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. * * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client" * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior. */ 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. * * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being * set here. */ 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 Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { safeTransfer(token, to, value); } else if (!token.transferAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferFromAndCallRelaxed( IERC1363 token, address from, address to, uint256 value, bytes memory data ) internal { if (to.code.length == 0) { safeTransferFrom(token, from, to, value); } else if (!token.transferFromAndCall(from, to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}. * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall} * once without retrying, and relies on the returned value to be true. * * Reverts if the returned value is other than `true`. */ function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { forceApprove(token, to, value); } else if (!token.approveAndCall(to, value, data)) { 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 {_callOptionalReturnBool} that reverts if call fails to meet the requirements. */ function _callOptionalReturn(IERC20 token, bytes memory data) private { uint256 returnSize; uint256 returnValue; assembly ("memory-safe") { let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20) // bubble errors if iszero(success) { let ptr := mload(0x40) returndatacopy(ptr, 0, returndatasize()) revert(ptr, returndatasize()) } returnSize := returndatasize() returnValue := mload(0) } if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) { 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 silently catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { bool success; uint256 returnSize; uint256 returnValue; assembly ("memory-safe") { success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20) returnSize := returndatasize() returnValue := mload(0) } return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[ERC]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; contract SaraLiquidityManager is Ownable, AccessControl { using SafeERC20 for IERC20; mapping(address => uint256) public creatorTokenReserves; mapping(address => uint256) public sonicReserves; mapping(address => uint256) public collectedFees; address public sonicToken; uint256 public constant REBALANCE_INTERVAL = 24 hours; uint256 public lastRebalanceTimestamp; uint256 public constant ENGAGEMENT_THRESHOLD = 1000; // 10% in basis points struct EngagementMetrics { uint256 lastSubscriberCount; uint256 smoothedSubscriberCount; uint256 lastUpdateTime; uint256 updateCount; } mapping(address => EngagementMetrics) public tokenEngagement; // Add constant for minimum liquidity uint256 public constant MIN_LIQUIDITY = 1000 * 1e18; // 1000 tokens minimum liquidity // Add new event event FeesRedeployed( address indexed token, uint256 amount, address indexed targetPool ); // Add minimum fee threshold for redeployment uint256 public constant MIN_FEES_FOR_REDEPLOY = 100 * 1e18; // 100 tokens // Add new struct for token price discovery struct PriceDiscoveryData { uint256 initialSubscribers; uint256 currentSubscribers; uint256 observationStartTime; bool isInDiscovery; uint256[] engagementSnapshots; } // Add mapping for price discovery mapping(address => PriceDiscoveryData) public priceDiscovery; // Constants for price discovery uint256 public constant DISCOVERY_PERIOD = 24 hours; uint256 public constant SNAPSHOT_INTERVAL = 4 hours; uint256 public constant MAX_SNAPSHOTS = 6; // 24 hours / 4 hours // Events event PriceDiscoveryStarted(address indexed token, uint256 initialSubscribers); event PriceDiscoveryCompleted( address indexed token, uint256 finalPrice, uint256 initialLiquidity ); event EngagementSnapshotTaken( address indexed token, uint256 subscribers, uint256 timestamp ); // Add array to track all pools address[] public trackedPools; mapping(address => bool) public isTrackedPool; // Add event for pool tracking event PoolTracked(address indexed pool); event PoolUntracked(address indexed pool); // Add constant for minimum token price uint256 public constant MIN_TOKEN_PRICE = 0.1e18; // 0.1 S tokens minimum price // Add price protection constants uint256 public constant MAX_PRICE_MULTIPLIER = 3; // 3x cap on price increase uint256 public constant BASE_PRICE = 1e18; // 1 S token base price // Add new event for auto fee redeployment event AutoFeesRedeployed( address indexed token, uint256 amount, address indexed targetPool, uint256 timestamp ); // Add constants for smoothing uint256 private constant WEIGHT_PREVIOUS = 80; uint256 private constant WEIGHT_NEW = 20; uint256 private constant REBALANCE_THRESHOLD = 110; // 10% above smoothed value uint256 private constant MIN_UPDATES_BEFORE_REBALANCE = 3; // Add event for smoothed metrics event EngagementSmoothed( address indexed token, uint256 rawCount, uint256 smoothedCount, uint256 timestamp ); // Add trading activity tracking struct PoolActivity { uint256 lastTradeTimestamp; uint256 tradingVolume24h; uint256 lastVolumeUpdateTime; } mapping(address => PoolActivity) public poolActivity; // Add constants for activity checks uint256 public constant ACTIVITY_THRESHOLD = 7 days; uint256 public constant MIN_24H_VOLUME = 1000 * 1e18; // 1000 tokens minimum volume // Add event for activity updates event PoolActivityUpdated( address indexed pool, uint256 volume24h, uint256 timestamp ); // Add error messages as constants string private constant ERR_NO_FEES = "No fees available to redeploy"; string private constant ERR_BELOW_MIN = "Below minimum fee threshold"; // Add DEX reference address public dex; // Define roles as public constants bytes32 public constant DEX_ROLE = keccak256("DEX_ROLE"); // Add state variables for tracking most needy pool struct PoolNeedInfo { address pool; uint256 score; uint256 lastUpdateTime; } PoolNeedInfo public mostNeededPool; uint256 public constant POOL_NEED_UPDATE_INTERVAL = 1 hours; // Add event for pool need updates event PoolNeedUpdated( address indexed pool, uint256 score, uint256 timestamp ); constructor(address _sonicToken) Ownable(msg.sender) { sonicToken = _sonicToken; // Grant roles to deployer _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(DEX_ROLE, msg.sender); } event LiquidityAdded(address indexed creatorToken, uint256 amountCreator, uint256 amountS); event LiquidityRemoved(address indexed creatorToken, uint256 amountCreator, uint256 amountS); event FeesCollected(address indexed token, uint256 amount); event LiquidityRebalanced(address indexed token, uint256 newReserveS); event FeesWithdrawn(address indexed token, uint256 amount); // Add function to track new pool function addPoolToTracking(address pool) internal { if (!isTrackedPool[pool]) { trackedPools.push(pool); isTrackedPool[pool] = true; emit PoolTracked(pool); } } // Add helper function function isNativeS(address token) internal pure returns (bool) { return token == address(0) || token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; } /** * @dev Adds liquidity to a pool with proper validation * @param creatorToken The creator token address * @param amountCreator Amount of creator tokens to add * @param amountS Amount of S tokens to add */ function addLiquidity( address creatorToken, uint256 amountCreator, uint256 amountS ) external payable onlyOwner { // Validate inputs require(amountCreator > 0 && amountS > 0, "Invalid liquidity amounts"); require(msg.value == amountS, "Incorrect S amount sent"); // Check creator token allowance first require( IERC20(creatorToken).allowance(msg.sender, address(this)) >= amountCreator, "Insufficient creator token allowance" ); // Check creator token balance require( IERC20(creatorToken).balanceOf(msg.sender) >= amountCreator, "Insufficient creator token balance" ); // Check minimum liquidity requirements require( creatorTokenReserves[creatorToken] + amountCreator >= MIN_LIQUIDITY, "Below minimum liquidity" ); require( sonicReserves[creatorToken] + amountS >= MIN_LIQUIDITY, "Below minimum liquidity" ); // Effects - Update reserves creatorTokenReserves[creatorToken] += amountCreator; sonicReserves[creatorToken] += amountS; // Track pool if new addPoolToTracking(creatorToken); // Interactions - Transfer creator tokens last IERC20(creatorToken).safeTransferFrom(msg.sender, address(this), amountCreator); emit LiquidityAdded(creatorToken, amountCreator, amountS); } // Update removeLiquidity function removeLiquidity( address creatorToken, uint256 amountCreator, uint256 amountS ) external onlyOwner { // Checks require(creatorTokenReserves[creatorToken] >= amountCreator, "Insufficient creator token liquidity"); require(sonicReserves[creatorToken] >= amountS, "Insufficient S token liquidity"); require(address(this).balance >= amountS, "Insufficient S balance"); // Add minimum liquidity check require( creatorTokenReserves[creatorToken] - amountCreator >= MIN_LIQUIDITY, "Cannot remove all liquidity" ); require( sonicReserves[creatorToken] - amountS >= MIN_LIQUIDITY, "Cannot remove all liquidity" ); // Effects creatorTokenReserves[creatorToken] -= amountCreator; sonicReserves[creatorToken] -= amountS; // Interactions IERC20(creatorToken).safeTransfer(msg.sender, amountCreator); (bool success,) = msg.sender.call{value: amountS}(""); require(success, "S token transfer failed"); emit LiquidityRemoved(creatorToken, amountCreator, amountS); } function collectFees(address token, uint256 amount) external { collectedFees[token] += amount; emit FeesCollected(token, amount); } /** * @dev Returns the current reserves for a creator token pair * @param creatorToken The address of the creator token * @return creatorReserve The amount of creator tokens in reserve * @return sReserve The amount of S tokens in reserve */ function getReserves( address creatorToken ) external view returns ( uint256 creatorReserve, // Amount of creator tokens uint256 sReserve // Amount of S tokens ) { creatorReserve = creatorTokenReserves[creatorToken]; sReserve = sonicReserves[creatorToken]; } function withdrawFees(address token, uint256 amount) external onlyOwner { // Checks require(collectedFees[token] >= amount, "Insufficient fees"); // Effects collectedFees[token] -= amount; // Interactions IERC20(token).safeTransfer(msg.sender, amount); emit FeesWithdrawn(token, amount); } /** * @dev Calculates smoothed engagement using weighted moving average */ function calculateSmoothedEngagement( uint256 previous, uint256 current ) internal pure returns (uint256) { return ( (previous * WEIGHT_PREVIOUS / 100) + (current * WEIGHT_NEW / 100) ); } /** * @dev Updates engagement metrics with smoothing */ function updateEngagementMetrics( address token, uint256 newSubscriberCount ) external onlyOwner { EngagementMetrics storage metrics = tokenEngagement[token]; // Initialize if first update if (metrics.lastSubscriberCount == 0) { metrics.lastSubscriberCount = newSubscriberCount; metrics.smoothedSubscriberCount = newSubscriberCount; metrics.updateCount = 1; return; } // Calculate smoothed value uint256 newSmoothedCount = calculateSmoothedEngagement( metrics.smoothedSubscriberCount, newSubscriberCount ); // Update metrics metrics.lastSubscriberCount = newSubscriberCount; metrics.smoothedSubscriberCount = newSmoothedCount; metrics.lastUpdateTime = block.timestamp; metrics.updateCount++; emit EngagementSmoothed( token, newSubscriberCount, newSmoothedCount, block.timestamp ); // Check if rebalance needed (only after minimum updates) if (metrics.updateCount >= MIN_UPDATES_BEFORE_REBALANCE) { uint256 rebalanceThreshold = (metrics.smoothedSubscriberCount * REBALANCE_THRESHOLD) / 100; if (newSubscriberCount > rebalanceThreshold) { _rebalanceLiquidity(token, metrics.smoothedSubscriberCount); } } } /** * @dev View function to get smoothed engagement metrics */ function getSmoothedMetrics( address token ) external view returns ( uint256 lastCount, uint256 smoothedCount, uint256 updateCount, bool eligibleForRebalance ) { EngagementMetrics storage metrics = tokenEngagement[token]; return ( metrics.lastSubscriberCount, metrics.smoothedSubscriberCount, metrics.updateCount, metrics.updateCount >= MIN_UPDATES_BEFORE_REBALANCE ); } /** * @dev Optimized rebalancing with minimum liquidity check */ function _rebalanceLiquidity(address token, uint256 subscriberCount) internal { // Skip if pool has insufficient liquidity if (sonicReserves[token] < MIN_LIQUIDITY) { emit RebalanceSkipped(token, sonicReserves[token], "Insufficient liquidity"); return; } // AI-based liquidity calculation uint256 newReserveS = calculateOptimalLiquidity(subscriberCount); // Update liquidity sonicReserves[token] = newReserveS; emit LiquidityRebalanced(token, newReserveS); } function calculateOptimalLiquidity(uint256 subscriberCount) internal pure returns (uint256) { // Initial simple formula, can be enhanced with AI later return subscriberCount * 1e18; } // Daily rebalance check function checkAndRebalance(address token) external { require( block.timestamp >= lastRebalanceTimestamp + REBALANCE_INTERVAL, "Too early for rebalance" ); EngagementMetrics storage metrics = tokenEngagement[token]; _rebalanceLiquidity(token, metrics.lastSubscriberCount); lastRebalanceTimestamp = block.timestamp; } /** * @dev Redeploys collected fees back into liquidity pools * @param token Token to redeploy fees for * @param targetPool Pool to add liquidity to */ function redeployFees( address token, address targetPool ) public onlyOwner { // Get fee amount first uint256 feeAmount = collectedFees[token]; // Validate fees require(feeAmount > 0, ERR_NO_FEES); require(feeAmount >= MIN_FEES_FOR_REDEPLOY, ERR_BELOW_MIN); // Calculate optimal deployment amount uint256 deployAmount = calculateOptimalDeployment(targetPool, feeAmount); require(deployAmount > 0, "No deployment needed"); require(deployAmount <= feeAmount, "Deploy amount exceeds available fees"); // Update state before transfers (CEI pattern) collectedFees[token] -= deployAmount; // Handle native S token differently if (isNativeS(token)) { require(address(this).balance >= deployAmount, "Insufficient S balance"); sonicReserves[targetPool] += deployAmount; emit FeesRedeployed(token, deployAmount, targetPool); return; } // Handle creator tokens require( IERC20(token).balanceOf(address(this)) >= deployAmount, "Insufficient token balance" ); uint256 sTokenAmount = calculateEquivalentSAmount(token, deployAmount); require(address(this).balance >= sTokenAmount, "Insufficient S balance"); // Update reserves creatorTokenReserves[targetPool] += deployAmount; sonicReserves[targetPool] += sTokenAmount; emit FeesRedeployed(token, deployAmount, targetPool); } /** * @dev Calculates optimal amount of fees to deploy based on pool needs */ function calculateOptimalDeployment( address pool, uint256 availableFees ) public view returns (uint256) { // Get current pool metrics EngagementMetrics storage metrics = tokenEngagement[pool]; // If pool has low liquidity relative to engagement, deploy more fees uint256 currentLiquidity = sonicReserves[pool]; uint256 optimalLiquidity = calculateOptimalLiquidity(metrics.lastSubscriberCount); if (currentLiquidity >= optimalLiquidity) { return 0; // No deployment needed } uint256 liquidityNeeded = optimalLiquidity - currentLiquidity; return liquidityNeeded < availableFees ? liquidityNeeded : availableFees; } /** * @dev Calculates equivalent S token amount for creator token amount */ function calculateEquivalentSAmount( address token, uint256 amount ) internal view returns (uint256) { // Use current pool ratio to calculate equivalent S amount uint256 creatorReserve = creatorTokenReserves[token]; uint256 sReserve = sonicReserves[token]; if (creatorReserve == 0) return amount; // 1:1 for new pools return (amount * sReserve) / creatorReserve; } /** * @dev Auto-redeploys fees with additional checks */ function autoRedeployFees(address token) external { uint256 feeAmount = collectedFees[token]; // Validate fees with clear error messages require(feeAmount > 0, ERR_NO_FEES); require(feeAmount >= MIN_FEES_FOR_REDEPLOY, ERR_BELOW_MIN); // Find optimal pool address targetPool = findOptimalRedeploymentPool(); require(targetPool != address(0), "No suitable pool found"); // Calculate deployment amount uint256 deployAmount = calculateOptimalDeployment(targetPool, feeAmount); require(deployAmount > 0, "No deployment needed"); require(deployAmount <= feeAmount, "Deploy amount exceeds available fees"); // Emit event before state changes emit AutoFeesRedeployed( token, deployAmount, targetPool, block.timestamp ); // Call redeployFees with validated amounts redeployFees(token, targetPool); } /** * @dev Optimized pool selection using cached value */ function findOptimalRedeploymentPool() internal returns (address) { // Return cached value if recent enough if ( mostNeededPool.pool != address(0) && block.timestamp < mostNeededPool.lastUpdateTime + POOL_NEED_UPDATE_INTERVAL ) { // Verify pool is still valid if ( isTrackedPool[mostNeededPool.pool] && isPoolActive(mostNeededPool.pool) ) { return mostNeededPool.pool; } } // If cache is stale or invalid, update it updateMostNeededPool(); return mostNeededPool.pool; } /** * @dev Updates the cached most needy pool */ function updateMostNeededPool() internal { // Reset current best mostNeededPool.pool = address(0); mostNeededPool.score = 0; // Cache array length uint256 poolCount = trackedPools.length; if (poolCount == 0) return; // Cache current time uint256 currentTime = block.timestamp; uint256 activityCutoff = currentTime - ACTIVITY_THRESHOLD; // Single pass through pools for (uint256 i = 0; i < poolCount; i++) { address pool = trackedPools[i]; // Skip inactive pools PoolActivity memory activity = poolActivity[pool]; if ( !isTrackedPool[pool] || activity.lastTradeTimestamp < activityCutoff || activity.tradingVolume24h < MIN_24H_VOLUME ) { continue; } // Calculate pool score uint256 score = calculatePoolScore(pool); // Update if better score found if (score > mostNeededPool.score) { mostNeededPool.pool = pool; mostNeededPool.score = score; } } // Update timestamp and emit event mostNeededPool.lastUpdateTime = currentTime; emit PoolNeedUpdated( mostNeededPool.pool, mostNeededPool.score, currentTime ); } /** * @dev Force update of pool needs (public for external triggers) */ function forcePoolNeedUpdate() external onlyOwner { updateMostNeededPool(); } /** * @dev View function to get current pool need info */ function getPoolNeedInfo() external view returns ( address pool, uint256 score, uint256 lastUpdate, bool isStale ) { return ( mostNeededPool.pool, mostNeededPool.score, mostNeededPool.lastUpdateTime, block.timestamp >= mostNeededPool.lastUpdateTime + POOL_NEED_UPDATE_INTERVAL ); } /** * @dev Allows removal of inactive pools from tracking */ function removePoolFromTracking(address pool) external onlyOwner { require(isTrackedPool[pool], "Pool not tracked"); // Find and remove from array for (uint i = 0; i < trackedPools.length; i++) { if (trackedPools[i] == pool) { // Move last element to this position (unless we're at the end) if (i != trackedPools.length - 1) { trackedPools[i] = trackedPools[trackedPools.length - 1]; } trackedPools.pop(); break; } } isTrackedPool[pool] = false; emit PoolUntracked(pool); } /** * @dev View function to get all tracked pools */ function getTrackedPools() external view returns (address[] memory) { return trackedPools; } /** * @dev Starts price discovery period for a new token */ function startPriceDiscovery( address token, uint256 initialSubscribers ) external onlyOwner { require(!priceDiscovery[token].isInDiscovery, "Already in discovery"); priceDiscovery[token] = PriceDiscoveryData({ initialSubscribers: initialSubscribers, currentSubscribers: initialSubscribers, observationStartTime: block.timestamp, isInDiscovery: true, engagementSnapshots: new uint256[](0) }); emit PriceDiscoveryStarted(token, initialSubscribers); } /** * @dev Records engagement snapshot during discovery period */ function recordEngagementSnapshot( address token, uint256 currentSubscribers ) external onlyOwner { PriceDiscoveryData storage data = priceDiscovery[token]; require(data.isInDiscovery, "Not in discovery"); require( data.engagementSnapshots.length < MAX_SNAPSHOTS, "Max snapshots reached" ); data.engagementSnapshots.push(currentSubscribers); data.currentSubscribers = currentSubscribers; emit EngagementSnapshotTaken(token, currentSubscribers, block.timestamp); // If we have enough snapshots, complete discovery if (data.engagementSnapshots.length == MAX_SNAPSHOTS) { completePriceDiscovery(token); } } /** * @dev Completes price discovery and sets initial liquidity */ function completePriceDiscovery(address token) public onlyOwner { PriceDiscoveryData storage data = priceDiscovery[token]; require(data.isInDiscovery, "Not in discovery"); require( block.timestamp >= data.observationStartTime + DISCOVERY_PERIOD, "Discovery period not complete" ); // Calculate optimal initial price based on engagement trends uint256 initialPrice = calculateInitialPrice(token); uint256 initialLiquidity = calculateInitialLiquidity( data.currentSubscribers, initialPrice ); // Set initial liquidity sonicReserves[token] = initialLiquidity; // Mark discovery as complete data.isInDiscovery = false; emit PriceDiscoveryCompleted(token, initialPrice, initialLiquidity); } /** * @dev Calculates initial price based on engagement trends with circuit breaker */ function calculateInitialPrice( address token ) internal returns (uint256) { PriceDiscoveryData storage data = priceDiscovery[token]; // Calculate engagement growth rate uint256 totalGrowth = 0; uint256 positiveSnapshots = 0; for (uint256 i = 0; i < data.engagementSnapshots.length; i++) { if (i > 0) { // Handle potential negative growth if (data.engagementSnapshots[i] > data.engagementSnapshots[i-1]) { uint256 growth = ((data.engagementSnapshots[i] - data.engagementSnapshots[i-1]) * 10000) / data.engagementSnapshots[i-1]; // Cap individual growth rate growth = growth > 10000 ? 10000 : growth; // Cap at 100% per snapshot totalGrowth += growth; positiveSnapshots++; } } } // Calculate average growth, defaulting to 0 if no positive growth uint256 avgGrowth = positiveSnapshots > 0 ? totalGrowth / positiveSnapshots : 0; // Calculate price with growth rate uint256 calculatedPrice = BASE_PRICE + ((BASE_PRICE * avgGrowth) / 10000); // Calculate maximum allowed price uint256 maxAllowedPrice = BASE_PRICE * MAX_PRICE_MULTIPLIER; // Apply circuit breaker logic if (calculatedPrice > maxAllowedPrice) { emit PriceCapReached(token, calculatedPrice, maxAllowedPrice); calculatedPrice = maxAllowedPrice; } return calculatedPrice > MIN_TOKEN_PRICE ? calculatedPrice : MIN_TOKEN_PRICE; } // Add event for monitoring price caps event PriceCapReached( address indexed token, uint256 calculatedPrice, uint256 cappedPrice ); // Add event for fallback pool selection event FallbackPoolSelected( address indexed pool, uint256 reserveAmount ); // Add event for skipped rebalances event RebalanceSkipped( address indexed token, uint256 currentLiquidity, string reason ); // Enhanced pool selection event event PoolSelected( address indexed pool, uint256 score, uint256 currentLiquidity ); /** * @dev Updates pool trading activity */ function updatePoolActivity( address pool, uint256 tradeAmount ) external onlyDEX { PoolActivity storage activity = poolActivity[pool]; // Update last trade timestamp activity.lastTradeTimestamp = block.timestamp; // Update 24h volume if (block.timestamp >= activity.lastVolumeUpdateTime + 24 hours) { // Reset volume after 24h activity.tradingVolume24h = tradeAmount; activity.lastVolumeUpdateTime = block.timestamp; } else { activity.tradingVolume24h += tradeAmount; } emit PoolActivityUpdated( pool, activity.tradingVolume24h, block.timestamp ); } /** * @dev Checks if pool is actively traded */ function isPoolActive(address pool) public view returns (bool) { // Cache struct to save gas PoolActivity storage activity = poolActivity[pool]; uint256 lastTrade = activity.lastTradeTimestamp; uint256 volume = activity.tradingVolume24h; // Short circuit evaluation saves gas if (lastTrade < block.timestamp - ACTIVITY_THRESHOLD) return false; if (volume < MIN_24H_VOLUME) return false; return true; } /** * @dev Calculates pool score based on liquidity and activity */ function calculatePoolScore( address pool ) internal view returns (uint256) { // Cache values to save gas uint256 liquidity = sonicReserves[pool]; uint256 volume = poolActivity[pool].tradingVolume24h; // Use bit shifts for multiplication (more gas efficient) // 70% = multiply by 7/10 // 30% = multiply by 3/10 return ( (liquidity * 7 + volume * 3) / 10 ); } /** * @dev View function to get pool activity metrics */ function getPoolActivity( address pool ) external view returns ( uint256 lastTrade, uint256 volume24h, bool isActive ) { PoolActivity storage activity = poolActivity[pool]; return ( activity.lastTradeTimestamp, activity.tradingVolume24h, isPoolActive(pool) ); } // Add function to set DEX address function setDEX(address _dex) external onlyOwner { require(_dex != address(0), "Invalid DEX address"); dex = _dex; } // Move calculateInitialLiquidity before it's used function calculateInitialLiquidity( uint256 subscriberCount, uint256 price ) public pure returns (uint256) { return subscriberCount * price; } // Update onlyDEX modifier to use role modifier onlyDEX() { require(hasRole(DEX_ROLE, msg.sender), "Caller is not DEX"); _; } // Add function to grant DEX role (only admin) function grantDEXRole(address dex) external onlyOwner { grantRole(DEX_ROLE, dex); } // Add function to revoke DEX role (only admin) function revokeDEXRole(address dex) external onlyOwner { revokeRole(DEX_ROLE, dex); } // Add receive function to accept native S token receive() external payable {} // Add fallback function to accept native S token with data fallback() external payable {} }
{ "optimizer": { "enabled": true, "runs": 200 }, "viaIR": true, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
[{"inputs":[{"internalType":"address","name":"_sonicToken","type":"address"},{"internalType":"address payable","name":"_liquidityManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldThreshold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newThreshold","type":"uint256"},{"indexed":true,"internalType":"address","name":"updatedBy","type":"address"}],"name":"AutoRedeployThresholdUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"ContractPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"ContractUnpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeesWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldManager","type":"address"},{"indexed":true,"internalType":"address","name":"newManager","type":"address"},{"indexed":true,"internalType":"address","name":"updatedBy","type":"address"}],"name":"LiquidityManagerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldSlippage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newSlippage","type":"uint256"},{"indexed":true,"internalType":"address","name":"updater","type":"address"}],"name":"MaxSlippageUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldSlippage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newSlippage","type":"uint256"},{"indexed":true,"internalType":"address","name":"updatedBy","type":"address"}],"name":"SlippageUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"creatorToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"SwapExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFee","type":"uint256"},{"indexed":true,"internalType":"address","name":"updatedBy","type":"address"}],"name":"SwapFeesUpdated","type":"event"},{"inputs":[],"name":"ABSOLUTE_MAX_SLIPPAGE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"AUTO_REDEPLOY_THRESHOLD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_MAX_SLIPPAGE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAXIMUM_ALLOWED_SLIPPAGE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SINGLE_SWAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SWAP_AMOUNT_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_LIQUIDITY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_TIME_BETWEEN_SWAPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"to","type":"address"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"reserveIn","type":"uint256"},{"internalType":"uint256","name":"reserveOut","type":"uint256"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getFeeRate","outputs":[{"internalType":"uint256","name":"feeNumerator","type":"uint256"},{"internalType":"uint256","name":"feeDenominator","type":"uint256"},{"internalType":"string","name":"description","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getPauseStatus","outputs":[{"internalType":"bool","name":"isPaused","type":"bool"},{"internalType":"uint256","name":"pauseDuration","type":"uint256"},{"internalType":"uint256","name":"lastPause","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSlippageSettings","outputs":[{"internalType":"uint256","name":"defaultSlippage","type":"uint256"},{"internalType":"uint256","name":"currentMaxSlippage","type":"uint256"},{"internalType":"uint256","name":"absoluteMaximum","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastPauseTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastSwapTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityManager","outputs":[{"internalType":"contract SaraLiquidityManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"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":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sonicToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"storedFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"creatorToken","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"uint256","name":"maxSlippage","type":"uint256"}],"name":"swapCreatorTokenForS","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"swapFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newThreshold","type":"uint256"}],"name":"updateAutoRedeployThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newManager","type":"address"}],"name":"updateLiquidityManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMaxSlippage","type":"uint256"}],"name":"updateMaxSlippage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"updateSwapFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"slippage","type":"uint256"}],"name":"validateSlippage","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawStoredFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60a03461013c57601f611c8f38819003918201601f19168301916001600160401b0383118484101761014157808492604094855283398101031261013c5780516001600160a01b038116919082900361013c57602001516001600160a01b0381169081900361013c5733156101265760008054336001600160a01b0319821681178355604051949290916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3601e600355683635c9adc5dea000006006556101f46080526103e860075560018060a01b0319600154161760015560018060a01b03196002541617600255611b37908161015882396080518181816101cb01528181610362015281816104d00152818161133d01526119340152f35b631e4fbdf760e01b600052600060045260246000fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe608080604052600436101561001d575b50361561001b57600080fd5b005b60003560e01c90816302e9d3e814610d3457508063054d50d414610d08578063074872c914610c5c57806311a9992e14610c40578063125c120414610c0657806313079df514610b055780631d9023cb14610ab9578063206f248d146109fd57806321b77d63146106b357806333827438146109d45780633f4ba83a14610948578063461a3a951461091f57806354cf2aeb14610901578063551512de146106db5780635c975abb146106b85780636604b53a146106b35780636611cb3614610646578063715018a6146105ed5780638456cb591461055657806384e5eed0146104f357806385015e09146104b85780638ac058cc1461049b5780638da5cb5b1461047257806390cd3694146104545780639a5901de14610436578063bf16f65a14610392578063c54e1d3814610346578063d855dc7e1461032a578063e7b0444c1461030c578063f2c75fa0146102cd578063f2fde38b146102445763f896acc71461018a573861000f565b3461023f57602036600319011261023f576004356101a66119c8565b600754906101be6101b5611884565b83831415611998565b6101f26101c96118bf565b7f0000000000000000000000000000000000000000000000000000000000000000831015611998565b6102086101fd6118fa565b6107d0831115611998565b8060075560405191825260208201527fc2590c5cad7b84e9fae727e107e2bb810de8248ada2afbc3d638140e187c004060403392a2005b600080fd5b3461023f57602036600319011261023f5761025d610d6a565b6102656119c8565b6001600160a01b031680156102b757600080546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b600052600060045260246000fd5b3461023f57602036600319011261023f576102e9600435611929565b9061030860405192839215158352604060208401526040830190610da4565b0390f35b3461023f57600036600319011261023f576020600754604051908152f35b3461023f57600036600319011261023f57602060405160058152f35b3461023f57600036600319011261023f576060600754604051907f0000000000000000000000000000000000000000000000000000000000000000825260208201526107d06040820152f35b3461023f57602036600319011261023f576004356103ae6119c8565b8015158061042a575b156103f757600354908060035560405191825260208201527f3b70f7ec6235fb8b9f4b0a4bf6fec9a5f46a7afc052d86ab34a8b9474bde94a160403392a2005b60405162461bcd60e51b815260206004820152600b60248201526a496e76616c69642066656560a81b6044820152606490fd5b506101f48111156103b7565b3461023f57600036600319011261023f576020600954604051908152f35b3461023f57600036600319011261023f576020600654604051908152f35b3461023f57600036600319011261023f576000546040516001600160a01b039091168152602090f35b3461023f57600036600319011261023f5760206040516107d08152f35b3461023f57600036600319011261023f5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461023f57600036600319011261023f576060604061030881516105178382610f37565b6014815273302e32352520666565202832352f31303030302960601b602082015282519384936019855261271060208601528401526060830190610da4565b3461023f57600036600319011261023f5761056f6119c8565b60085460ff81166105b75760019060ff191617600855426009556040514281527f80170b5fcdd2bf1e0660ef4b8851f86685f64d41b1a19de1471947ece8725aac60203392a2005b60405162461bcd60e51b815260206004820152600e60248201526d105b1c9958591e481c185d5cd95960921b6044820152606490fd5b3461023f57600036600319011261023f576106066119c8565b600080546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b608036600319011261023f5761065a610d6a565b60ff600854166106795761001b906064359060443590602435906110e2565b60405162461bcd60e51b815260206004820152601260248201527110dbdb9d1c9858dd081a5cc81c185d5cd95960721b6044820152606490fd5b610d80565b3461023f57600036600319011261023f57602060ff600854166040519015158152f35b3461023f57606036600319011261023f576106f4610d6a565b6044356001600160a01b038116916024359183900361023f576107156119c8565b60ff60085416156108c657821561088d5761072f816119f1565b156107965760407fbbae553982d541d38dacb1ea468ac757f324d0e279b6e4a2958f9e894da575ff9161076484471015610ffb565b61077e6000808080888a5af1610778610f6f565b50610faf565b81519384524260208501526001600160a01b031692a3005b6040516370a0823160e01b81523060048201526001600160a01b03821690602081602481855afa801561088157849160009161084c575b5010610807577fbbae553982d541d38dacb1ea468ac757f324d0e279b6e4a2958f9e894da575ff916108028486604094611a22565b61077e565b60405162461bcd60e51b815260206004820152601a60248201527f496e73756666696369656e7420746f6b656e2062616c616e63650000000000006044820152606490fd5b9150506020813d602011610879575b8161086860209383610f37565b8101031261023f57839051866107cd565b3d915061085b565b6040513d6000823e3d90fd5b60405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b6044820152606490fd5b60405162461bcd60e51b815260206004820152601360248201527210dbdb9d1c9858dd081b9bdd081c185d5cd959606a1b6044820152606490fd5b3461023f57600036600319011261023f576020600354604051908152f35b3461023f57600036600319011261023f576001546040516001600160a01b039091168152602090f35b3461023f57600036600319011261023f576109616119c8565b60085460ff8116156109a25760ff19166008556040514281527f107553d8191d85b405879cf752997865edd48d94e20bda4dd27223c94b31a7cc60203392a2005b60405162461bcd60e51b815260206004820152600a602482015269139bdd081c185d5cd95960b21b6044820152606490fd5b3461023f57600036600319011261023f576002546040516001600160a01b039091168152602090f35b3461023f57602036600319011261023f57610a16610d6a565b610a1e6119c8565b6001600160a01b03168015610a7457600280546001600160a01b0319811683179091553391906001600160a01b03167fe7448546463da3f04e4f62d61edd1c362ac53d025bb9a341955227b25f1899f7600080a4005b60405162461bcd60e51b815260206004820152601760248201527f496e76616c6964206d616e6167657220616464726573730000000000000000006044820152606490fd5b3461023f57600036600319011261023f57606060ff6008541680600014610afe57610ae660095442610f2a565b60095490604051921515835260208301526040820152f35b6000610ae6565b3461023f57604036600319011261023f57610b1e610d6a565b602435610b296119c8565b60018060a01b038216918260005260056020528160406000205410610bc1577fc0819c13be868895eb93e40eaceb96de976442fa1d404e5c55f14bb65a8c489a91610b8f60209285600052600584526040600020610b88848254610f2a565b90556119f1565b15610bb157610ba8600080808085335af1610778610f6f565b604051908152a2005b610bbc813386611a22565b610ba8565b60405162461bcd60e51b815260206004820152601860248201527f496e73756666696369656e742073746f726564206665657300000000000000006044820152606490fd5b3461023f57602036600319011261023f576001600160a01b03610c27610d6a565b1660005260056020526020604060002054604051908152f35b3461023f57600036600319011261023f576020604051603c8152f35b3461023f57602036600319011261023f57600435610c786119c8565b683635c9adc5dea000008110610cc357600654908060065560405191825260208201527f77a6f4bf7cf4544531633fc1c2a3e8d2c53ad36fe80a723e124caec563a3335860403392a2005b60405162461bcd60e51b815260206004820152601760248201527f42656c6f77206d696e696d756d206c69717569646974790000000000000000006044820152606490fd5b3461023f57606036600319011261023f576020610d2c604435602435600435610e1b565b604051908152f35b3461023f57602036600319011261023f576020906001600160a01b03610d58610d6a565b16600052600482526040600020548152f35b600435906001600160a01b038216820361023f57565b3461023f57600036600319011261023f576020604051683635c9adc5dea000008152f35b919082519283825260005b848110610dd0575050826000602080949584010152601f8019910116010190565b80602080928401015182828601015201610daf565b81810292918115918404141715610df857565b634e487b7160e01b600052601160045260246000fd5b91908201809211610df857565b8015610eee57811592831580610ee5575b15610ead576126f782029182046126f703610df857610e4b9082610de5565b926127108302928304612710141715610df857610e6791610e0e565b908115610e72570490565b60405162461bcd60e51b815260206004820152601360248201527224b73b30b634b21031b0b631bab630ba34b7b760691b6044820152606490fd5b60405162461bcd60e51b815260206004820152601060248201526f496e76616c696420726573657276657360801b6044820152606490fd5b50801515610e2c565b60405162461bcd60e51b8152602060048201526014602482015273125b9d985b1a59081a5b9c1d5d08185b5bdd5b9d60621b6044820152606490fd5b91908203918211610df857565b90601f8019910116810190811067ffffffffffffffff821117610f5957604052565b634e487b7160e01b600052604160045260246000fd5b3d15610faa573d9067ffffffffffffffff8211610f595760405191610f9e601f8201601f191660200184610f37565b82523d6000602084013e565b606090565b15610fb657565b60405162461bcd60e51b815260206004820152601760248201527f5320746f6b656e207472616e73666572206661696c65640000000000000000006044820152606490fd5b1561100257565b60405162461bcd60e51b8152602060048201526016602482015275496e73756666696369656e7420532062616c616e636560501b6044820152606490fd5b919082604091031261023f576020825192015190565b1561105d57565b60405162461bcd60e51b815260206004820152601e60248201527f496e73756666696369656e74206c697175696469747920696e20706f6f6c00006044820152606490fd5b156110a957565b60405162461bcd60e51b81526020600482015260116024820152700a6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b6044820152606490fd5b929091336000526004602052604060002054603c8101809111610df857421061184e5760025460408051630fa6707960e21b81526001600160a01b039687166004820181905296909283916024918391165afa801561088157600091829161182d575b5061115282861115611056565b80156117f1576005820282810460051483151715610df8576064900485116117bb578415611785576040516370a0823160e01b81523360048201526020816024818a5afa8015610881578691600091611750575b5010611714576111b69185610e1b565b906111c3818310156110a2565b6111dd6127106111d560035485610de5565b048093610f2a565b92683635c9adc5dea0000085116113305750506111fc83303387611a62565b60018060a01b03600154166000526005602052604060002061121f828254610e0e565b90556001546001600160a01b031660008181526005602052604090205460065491939111156112b7575b7f28c738dbec11a1bed94ba127a3712d54bcd39cf4ae95b6ebd671aaf10fd0287b9250611280600080808085335af1610778610f6f565b336000526004602052426040600020556112b26040519283923396846040919493926060820195825260208201520152565b0390a3565b6002546001600160a01b031692833b1561023f5760009360248592604051968793849263bc99f48760e01b845260048401525af1928315610881577f28c738dbec11a1bed94ba127a3712d54bcd39cf4ae95b6ebd671aaf10fd0287b9361131f575b50611249565b600061132a91610f37565b38611319565b93959490936000935091507f0000000000000000000000000000000000000000000000000000000000000000865b6113745750505061137292935010156110a2565b565b909192683635c9adc5dea00000871160001461170e57683635c9adc5dea000005b6113a36007548611156110a2565b841561170857845b8381106116d057604051636eb1769f60e11b8152336004820152306024820152602081604481895afa8015610881578391600091611699575b50106116485760025460408051630fa6707960e21b81526004810188905291829060249082906001600160a01b03165afa8015610881576000918291611618575b5061143282851115611056565b838111156115d35761271061145661144f61145d938a9588610e1b565b9485610de5565b0483610f2a565b9161147c6114736127106111d560035485610de5565b938410156110a2565b61148883471015610ffb565b61149484303385611a62565b6114a8600080808087335af1610778610f6f565b60018060a01b0360015416600052600560205260406000206114cb828254610e0e565b90556001546001600160a01b03166000818152600560205260409020546006541115611568575b5093611558929161155e95336000526004602052426040600020557f28c738dbec11a1bed94ba127a3712d54bcd39cf4ae95b6ebd671aaf10fd0287b604051806115503394888b846040919493926060820195825260208201520152565b0390a3610e0e565b97610f2a565b959291908661135e565b60025491956001600160a01b03909216939250833b1561023f5760009360248592604051968793849263bc99f48760e01b845260048401525af180156108815761155e956115589489926115c2575b5091955091926114f2565b60006115cd91610f37565b386115b7565b60405162461bcd60e51b815260206004820152601e60248201527f496e73756666696369656e74205320746f6b656e206c697175696469747900006044820152606490fd5b905061163b915060403d8111611641575b6116338183610f37565b810190611040565b38611425565b503d611629565b60405162461bcd60e51b8152602060048201526024808201527f496e73756666696369656e742063726561746f7220746f6b656e20616c6c6f77604482015263616e636560e01b6064820152608490fd5b91506020823d82116116c8575b816116b360209383610f37565b810103126116c55750829051386113e4565b80fd5b3d91506116a6565b60405162461bcd60e51b815260206004820152601060248201526f496e76616c696420736c69707061676560801b6044820152606490fd5b826113ab565b86611395565b60405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606490fd5b9150506020813d60201161177d575b8161176c60209383610f37565b8101031261023f57859051386111a6565b3d915061175f565b60405162461bcd60e51b815260206004820152600e60248201526d125b9d985b1a5908185b5bdd5b9d60921b6044820152606490fd5b60405162461bcd60e51b815260206004820152600e60248201526d5377617020746f6f206c6172676560901b6044820152606490fd5b60405162461bcd60e51b81526020600482015260146024820152734e6f205320746f6b656e206c697175696469747960601b6044820152606490fd5b9050611848915060403d604011611641576116338183610f37565b38611145565b60405162461bcd60e51b815260206004820152600e60248201526d546f6f206d616e7920737761707360901b6044820152606490fd5b60405190611893604083610f37565b601c82527f4e657720736c6970706167652073616d652061732063757272656e74000000006020830152565b604051906118ce604083610f37565b601c82527f43616e6e6f74206265206c6f776572207468616e2064656661756c74000000006020830152565b60405190611909604083610f37565b601182527043616e6e6f74206578636565642032302560781b6020830152565b600754811461198f577f00000000000000000000000000000000000000000000000000000000000000008110611986576107d01061197957600190604051611972602082610f37565b6000815290565b6119816118fa565b600091565b506119816118bf565b50611981611884565b156119a05750565b60405162461bcd60e51b8152602060048201529081906119c4906024830190610da4565b0390fd5b6000546001600160a01b031633036119dc57565b63118cdaa760e01b6000523360045260246000fd5b6001600160a01b03168015908115611a07575090565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee91501490565b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448083019390935291815261137291611a5d606483610f37565b611aa6565b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815261137291611a5d608483610f37565b906000602091828151910182855af115610881576000513d611af857506001600160a01b0381163b155b611ad75750565b635274afe760e01b60009081526001600160a01b0391909116600452602490fd5b60011415611ad056fea264697066735822122076687da6740d8d7eb7f412f79f4015426af1a02ca243ffc70744408b1961ef3e64736f6c634300081c0033000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000372accd631a68ee06bdc97c6194eb26c7ee3460b
Deployed Bytecode
0x608080604052600436101561001d575b50361561001b57600080fd5b005b60003560e01c90816302e9d3e814610d3457508063054d50d414610d08578063074872c914610c5c57806311a9992e14610c40578063125c120414610c0657806313079df514610b055780631d9023cb14610ab9578063206f248d146109fd57806321b77d63146106b357806333827438146109d45780633f4ba83a14610948578063461a3a951461091f57806354cf2aeb14610901578063551512de146106db5780635c975abb146106b85780636604b53a146106b35780636611cb3614610646578063715018a6146105ed5780638456cb591461055657806384e5eed0146104f357806385015e09146104b85780638ac058cc1461049b5780638da5cb5b1461047257806390cd3694146104545780639a5901de14610436578063bf16f65a14610392578063c54e1d3814610346578063d855dc7e1461032a578063e7b0444c1461030c578063f2c75fa0146102cd578063f2fde38b146102445763f896acc71461018a573861000f565b3461023f57602036600319011261023f576004356101a66119c8565b600754906101be6101b5611884565b83831415611998565b6101f26101c96118bf565b7f00000000000000000000000000000000000000000000000000000000000001f4831015611998565b6102086101fd6118fa565b6107d0831115611998565b8060075560405191825260208201527fc2590c5cad7b84e9fae727e107e2bb810de8248ada2afbc3d638140e187c004060403392a2005b600080fd5b3461023f57602036600319011261023f5761025d610d6a565b6102656119c8565b6001600160a01b031680156102b757600080546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b600052600060045260246000fd5b3461023f57602036600319011261023f576102e9600435611929565b9061030860405192839215158352604060208401526040830190610da4565b0390f35b3461023f57600036600319011261023f576020600754604051908152f35b3461023f57600036600319011261023f57602060405160058152f35b3461023f57600036600319011261023f576060600754604051907f00000000000000000000000000000000000000000000000000000000000001f4825260208201526107d06040820152f35b3461023f57602036600319011261023f576004356103ae6119c8565b8015158061042a575b156103f757600354908060035560405191825260208201527f3b70f7ec6235fb8b9f4b0a4bf6fec9a5f46a7afc052d86ab34a8b9474bde94a160403392a2005b60405162461bcd60e51b815260206004820152600b60248201526a496e76616c69642066656560a81b6044820152606490fd5b506101f48111156103b7565b3461023f57600036600319011261023f576020600954604051908152f35b3461023f57600036600319011261023f576020600654604051908152f35b3461023f57600036600319011261023f576000546040516001600160a01b039091168152602090f35b3461023f57600036600319011261023f5760206040516107d08152f35b3461023f57600036600319011261023f5760206040517f00000000000000000000000000000000000000000000000000000000000001f48152f35b3461023f57600036600319011261023f576060604061030881516105178382610f37565b6014815273302e32352520666565202832352f31303030302960601b602082015282519384936019855261271060208601528401526060830190610da4565b3461023f57600036600319011261023f5761056f6119c8565b60085460ff81166105b75760019060ff191617600855426009556040514281527f80170b5fcdd2bf1e0660ef4b8851f86685f64d41b1a19de1471947ece8725aac60203392a2005b60405162461bcd60e51b815260206004820152600e60248201526d105b1c9958591e481c185d5cd95960921b6044820152606490fd5b3461023f57600036600319011261023f576106066119c8565b600080546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b608036600319011261023f5761065a610d6a565b60ff600854166106795761001b906064359060443590602435906110e2565b60405162461bcd60e51b815260206004820152601260248201527110dbdb9d1c9858dd081a5cc81c185d5cd95960721b6044820152606490fd5b610d80565b3461023f57600036600319011261023f57602060ff600854166040519015158152f35b3461023f57606036600319011261023f576106f4610d6a565b6044356001600160a01b038116916024359183900361023f576107156119c8565b60ff60085416156108c657821561088d5761072f816119f1565b156107965760407fbbae553982d541d38dacb1ea468ac757f324d0e279b6e4a2958f9e894da575ff9161076484471015610ffb565b61077e6000808080888a5af1610778610f6f565b50610faf565b81519384524260208501526001600160a01b031692a3005b6040516370a0823160e01b81523060048201526001600160a01b03821690602081602481855afa801561088157849160009161084c575b5010610807577fbbae553982d541d38dacb1ea468ac757f324d0e279b6e4a2958f9e894da575ff916108028486604094611a22565b61077e565b60405162461bcd60e51b815260206004820152601a60248201527f496e73756666696369656e7420746f6b656e2062616c616e63650000000000006044820152606490fd5b9150506020813d602011610879575b8161086860209383610f37565b8101031261023f57839051866107cd565b3d915061085b565b6040513d6000823e3d90fd5b60405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9958da5c1a595b9d607a1b6044820152606490fd5b60405162461bcd60e51b815260206004820152601360248201527210dbdb9d1c9858dd081b9bdd081c185d5cd959606a1b6044820152606490fd5b3461023f57600036600319011261023f576020600354604051908152f35b3461023f57600036600319011261023f576001546040516001600160a01b039091168152602090f35b3461023f57600036600319011261023f576109616119c8565b60085460ff8116156109a25760ff19166008556040514281527f107553d8191d85b405879cf752997865edd48d94e20bda4dd27223c94b31a7cc60203392a2005b60405162461bcd60e51b815260206004820152600a602482015269139bdd081c185d5cd95960b21b6044820152606490fd5b3461023f57600036600319011261023f576002546040516001600160a01b039091168152602090f35b3461023f57602036600319011261023f57610a16610d6a565b610a1e6119c8565b6001600160a01b03168015610a7457600280546001600160a01b0319811683179091553391906001600160a01b03167fe7448546463da3f04e4f62d61edd1c362ac53d025bb9a341955227b25f1899f7600080a4005b60405162461bcd60e51b815260206004820152601760248201527f496e76616c6964206d616e6167657220616464726573730000000000000000006044820152606490fd5b3461023f57600036600319011261023f57606060ff6008541680600014610afe57610ae660095442610f2a565b60095490604051921515835260208301526040820152f35b6000610ae6565b3461023f57604036600319011261023f57610b1e610d6a565b602435610b296119c8565b60018060a01b038216918260005260056020528160406000205410610bc1577fc0819c13be868895eb93e40eaceb96de976442fa1d404e5c55f14bb65a8c489a91610b8f60209285600052600584526040600020610b88848254610f2a565b90556119f1565b15610bb157610ba8600080808085335af1610778610f6f565b604051908152a2005b610bbc813386611a22565b610ba8565b60405162461bcd60e51b815260206004820152601860248201527f496e73756666696369656e742073746f726564206665657300000000000000006044820152606490fd5b3461023f57602036600319011261023f576001600160a01b03610c27610d6a565b1660005260056020526020604060002054604051908152f35b3461023f57600036600319011261023f576020604051603c8152f35b3461023f57602036600319011261023f57600435610c786119c8565b683635c9adc5dea000008110610cc357600654908060065560405191825260208201527f77a6f4bf7cf4544531633fc1c2a3e8d2c53ad36fe80a723e124caec563a3335860403392a2005b60405162461bcd60e51b815260206004820152601760248201527f42656c6f77206d696e696d756d206c69717569646974790000000000000000006044820152606490fd5b3461023f57606036600319011261023f576020610d2c604435602435600435610e1b565b604051908152f35b3461023f57602036600319011261023f576020906001600160a01b03610d58610d6a565b16600052600482526040600020548152f35b600435906001600160a01b038216820361023f57565b3461023f57600036600319011261023f576020604051683635c9adc5dea000008152f35b919082519283825260005b848110610dd0575050826000602080949584010152601f8019910116010190565b80602080928401015182828601015201610daf565b81810292918115918404141715610df857565b634e487b7160e01b600052601160045260246000fd5b91908201809211610df857565b8015610eee57811592831580610ee5575b15610ead576126f782029182046126f703610df857610e4b9082610de5565b926127108302928304612710141715610df857610e6791610e0e565b908115610e72570490565b60405162461bcd60e51b815260206004820152601360248201527224b73b30b634b21031b0b631bab630ba34b7b760691b6044820152606490fd5b60405162461bcd60e51b815260206004820152601060248201526f496e76616c696420726573657276657360801b6044820152606490fd5b50801515610e2c565b60405162461bcd60e51b8152602060048201526014602482015273125b9d985b1a59081a5b9c1d5d08185b5bdd5b9d60621b6044820152606490fd5b91908203918211610df857565b90601f8019910116810190811067ffffffffffffffff821117610f5957604052565b634e487b7160e01b600052604160045260246000fd5b3d15610faa573d9067ffffffffffffffff8211610f595760405191610f9e601f8201601f191660200184610f37565b82523d6000602084013e565b606090565b15610fb657565b60405162461bcd60e51b815260206004820152601760248201527f5320746f6b656e207472616e73666572206661696c65640000000000000000006044820152606490fd5b1561100257565b60405162461bcd60e51b8152602060048201526016602482015275496e73756666696369656e7420532062616c616e636560501b6044820152606490fd5b919082604091031261023f576020825192015190565b1561105d57565b60405162461bcd60e51b815260206004820152601e60248201527f496e73756666696369656e74206c697175696469747920696e20706f6f6c00006044820152606490fd5b156110a957565b60405162461bcd60e51b81526020600482015260116024820152700a6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b6044820152606490fd5b929091336000526004602052604060002054603c8101809111610df857421061184e5760025460408051630fa6707960e21b81526001600160a01b039687166004820181905296909283916024918391165afa801561088157600091829161182d575b5061115282861115611056565b80156117f1576005820282810460051483151715610df8576064900485116117bb578415611785576040516370a0823160e01b81523360048201526020816024818a5afa8015610881578691600091611750575b5010611714576111b69185610e1b565b906111c3818310156110a2565b6111dd6127106111d560035485610de5565b048093610f2a565b92683635c9adc5dea0000085116113305750506111fc83303387611a62565b60018060a01b03600154166000526005602052604060002061121f828254610e0e565b90556001546001600160a01b031660008181526005602052604090205460065491939111156112b7575b7f28c738dbec11a1bed94ba127a3712d54bcd39cf4ae95b6ebd671aaf10fd0287b9250611280600080808085335af1610778610f6f565b336000526004602052426040600020556112b26040519283923396846040919493926060820195825260208201520152565b0390a3565b6002546001600160a01b031692833b1561023f5760009360248592604051968793849263bc99f48760e01b845260048401525af1928315610881577f28c738dbec11a1bed94ba127a3712d54bcd39cf4ae95b6ebd671aaf10fd0287b9361131f575b50611249565b600061132a91610f37565b38611319565b93959490936000935091507f00000000000000000000000000000000000000000000000000000000000001f4865b6113745750505061137292935010156110a2565b565b909192683635c9adc5dea00000871160001461170e57683635c9adc5dea000005b6113a36007548611156110a2565b841561170857845b8381106116d057604051636eb1769f60e11b8152336004820152306024820152602081604481895afa8015610881578391600091611699575b50106116485760025460408051630fa6707960e21b81526004810188905291829060249082906001600160a01b03165afa8015610881576000918291611618575b5061143282851115611056565b838111156115d35761271061145661144f61145d938a9588610e1b565b9485610de5565b0483610f2a565b9161147c6114736127106111d560035485610de5565b938410156110a2565b61148883471015610ffb565b61149484303385611a62565b6114a8600080808087335af1610778610f6f565b60018060a01b0360015416600052600560205260406000206114cb828254610e0e565b90556001546001600160a01b03166000818152600560205260409020546006541115611568575b5093611558929161155e95336000526004602052426040600020557f28c738dbec11a1bed94ba127a3712d54bcd39cf4ae95b6ebd671aaf10fd0287b604051806115503394888b846040919493926060820195825260208201520152565b0390a3610e0e565b97610f2a565b959291908661135e565b60025491956001600160a01b03909216939250833b1561023f5760009360248592604051968793849263bc99f48760e01b845260048401525af180156108815761155e956115589489926115c2575b5091955091926114f2565b60006115cd91610f37565b386115b7565b60405162461bcd60e51b815260206004820152601e60248201527f496e73756666696369656e74205320746f6b656e206c697175696469747900006044820152606490fd5b905061163b915060403d8111611641575b6116338183610f37565b810190611040565b38611425565b503d611629565b60405162461bcd60e51b8152602060048201526024808201527f496e73756666696369656e742063726561746f7220746f6b656e20616c6c6f77604482015263616e636560e01b6064820152608490fd5b91506020823d82116116c8575b816116b360209383610f37565b810103126116c55750829051386113e4565b80fd5b3d91506116a6565b60405162461bcd60e51b815260206004820152601060248201526f496e76616c696420736c69707061676560801b6044820152606490fd5b826113ab565b86611395565b60405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606490fd5b9150506020813d60201161177d575b8161176c60209383610f37565b8101031261023f57859051386111a6565b3d915061175f565b60405162461bcd60e51b815260206004820152600e60248201526d125b9d985b1a5908185b5bdd5b9d60921b6044820152606490fd5b60405162461bcd60e51b815260206004820152600e60248201526d5377617020746f6f206c6172676560901b6044820152606490fd5b60405162461bcd60e51b81526020600482015260146024820152734e6f205320746f6b656e206c697175696469747960601b6044820152606490fd5b9050611848915060403d604011611641576116338183610f37565b38611145565b60405162461bcd60e51b815260206004820152600e60248201526d546f6f206d616e7920737761707360901b6044820152606490fd5b60405190611893604083610f37565b601c82527f4e657720736c6970706167652073616d652061732063757272656e74000000006020830152565b604051906118ce604083610f37565b601c82527f43616e6e6f74206265206c6f776572207468616e2064656661756c74000000006020830152565b60405190611909604083610f37565b601182527043616e6e6f74206578636565642032302560781b6020830152565b600754811461198f577f00000000000000000000000000000000000000000000000000000000000001f48110611986576107d01061197957600190604051611972602082610f37565b6000815290565b6119816118fa565b600091565b506119816118bf565b50611981611884565b156119a05750565b60405162461bcd60e51b8152602060048201529081906119c4906024830190610da4565b0390fd5b6000546001600160a01b031633036119dc57565b63118cdaa760e01b6000523360045260246000fd5b6001600160a01b03168015908115611a07575090565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee91501490565b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448083019390935291815261137291611a5d606483610f37565b611aa6565b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815261137291611a5d608483610f37565b906000602091828151910182855af115610881576000513d611af857506001600160a01b0381163b155b611ad75750565b635274afe760e01b60009081526001600160a01b0391909116600452602490fd5b60011415611ad056fea264697066735822122076687da6740d8d7eb7f412f79f4015426af1a02ca243ffc70744408b1961ef3e64736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000372accd631a68ee06bdc97c6194eb26c7ee3460b
-----Decoded View---------------
Arg [0] : _sonicToken (address): 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
Arg [1] : _liquidityManager (address): 0x372ACcD631A68eE06bDc97C6194Eb26c7Ee3460b
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
Arg [1] : 000000000000000000000000372accd631a68ee06bdc97c6194eb26c7ee3460b
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.