Blockchain / Global / Address

Contract

0x85078Bc4D896c953efBF0Ec7507e2e97bc20D8Dc

Balance
0 SOMI
Transactions sent
1
Type
Contract · 5,991 bytes of code

MigrationLocker Verified

Compiler
solc 0.8.27+commit.40a35a09
Language
Solidity
Optimization
Yes, 200 runs
EVM version
paris
License
—
Verified
2026-10-12

contracts/launchpad/MigrationLocker.sol

and 8 more files

Solidity
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.0;

import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IMigrationLocker} from "../interfaces/IMigrationLocker.sol";
import {IAMMFactory} from "../interfaces/IAMMFactory.sol";
import {IAMMRouter} from "../interfaces/IAMMRouter.sol";
import {IAMMPairERC20} from "../interfaces/IAMMPairERC20.sol";

import {TransferHelper} from "../libraries/TransferHelper.sol";

contract MigrationLocker is ReentrancyGuard, IMigrationLocker {
    using EnumerableSet for EnumerableSet.AddressSet;
    using Math for uint256;

    IAMMFactory public factory;
    IAMMRouter public router;
    address public migrator;
    EnumerableSet.AddressSet private lockedTokens;
    mapping(address => uint256[]) locksForToken;
    mapping(address => TokenLock[]) public tokenLocks;

    modifier onlyMigrator() {
        require(msg.sender == migrator, "FORBIDDEN");
        _;
    }

    constructor(
        IAMMFactory _factory,
        IAMMRouter _router,
        address _migrator
    ) {
        factory = _factory;
        router = _router;
        migrator = _migrator;
    }

    function lockLPToken(
        address _lpToken,
        address _withdrawer,
        uint256 _amount,
        uint256 _lockSeconds
    ) external override nonReentrant onlyMigrator {
        require(_lockSeconds < 10000000000, "timestamp invalid");
        require(_amount != 0, "amount invalid");
        _checkPair(_lpToken);

        TransferHelper.safeTransferFrom(
            _lpToken,
            msg.sender,
            address(this),
            _amount
        );
        (uint256 reserve0, uint256 reserve1, ) = IAMMPairERC20(_lpToken)
            .getReserves();
        TokenLock memory lock = TokenLock({
            lockDate: block.timestamp,
            unLockDate: block.timestamp + _lockSeconds,
            amount: _amount,
            remainAmount0: 0,
            remainAmount1: 0,
            k: (_amount * reserve0 * reserve1) /
                IAMMPairERC20(_lpToken).totalSupply(),
            lockID: tokenLocks[_lpToken].length,
            owner: _withdrawer,
            updateTimestamp: block.timestamp
        });
        tokenLocks[_lpToken].push(lock);
        lockedTokens.add(_lpToken);
        locksForToken[_lpToken].push(lock.lockID);

        emit Deposit(_lpToken, msg.sender, _amount, lock);
    }

    function allLockedTokens()
        external
        view
        returns (address[] memory lpTokens)
    {
        lpTokens = lockedTokens.values();
    }

    function allLockIDs(
        address _lpToken
    ) external view returns (uint256[] memory lockIDs) {
        lockIDs = locksForToken[_lpToken];
    }

    function claimeLpReward(
        address _lpToken,
        uint256 _index,
        uint256 _lockID,
        address _to
    ) external nonReentrant onlyMigrator {
        _claimeLpReward(_lpToken, _index, _lockID, _to);
    }

    function withdraw(
        address _lpToken,
        uint256 _index,
        uint256 _lockID
    ) external nonReentrant {
        require(locksForToken[_lpToken].length > 0, "lock mismatch");
        uint256 lockID = locksForToken[_lpToken][_index];
        TokenLock storage lock = tokenLocks[_lpToken][lockID];
        require(
            lockID == _lockID &&
                lock.lockID == lockID &&
                lock.owner == msg.sender,
            "lock mismatch"
        );
        require(lock.unLockDate < block.timestamp, "not yet");
        uint256[] storage lockIDs = locksForToken[_lpToken];
        lockIDs[_index] = lockIDs[lockIDs.length - 1];
        lockIDs.pop();
        if (lock.remainAmount0 != 0 || lock.remainAmount1 != 0) {
            (address token0, address token1) = (
                IAMMPairERC20(_lpToken).token0(),
                IAMMPairERC20(_lpToken).token1()
            );
            if (lock.remainAmount0 != 0) {
                TransferHelper.safeTransfer(
                    token0,
                    msg.sender,
                    lock.remainAmount0
                );
                lock.remainAmount0 = 0;
            }
            if (lock.remainAmount1 != 0) {
                TransferHelper.safeTransfer(
                    token1,
                    msg.sender,
                    lock.remainAmount1
                );
                lock.remainAmount1 = 0;
            }
        }
        TransferHelper.safeTransfer(_lpToken, msg.sender, lock.amount);
        lock.k = 0;
        lock.amount = 0;
        lock.updateTimestamp = block.timestamp;
        emit Withdraw(_lpToken, msg.sender, lock.amount, lock);
    }

    function _checkPair(address lpToken) internal view {
        IAMMPairERC20 pair = IAMMPairERC20(lpToken);
        address pairAddr = factory.getPair(pair.token0(), pair.token1());
        require(pairAddr == lpToken, "NOT AMM");
    }

    function _claimeLpReward(
        address _lpToken,
        uint256 _index,
        uint256 _lockID,
        address _to
    ) internal {
        uint256 lockID = locksForToken[_lpToken][_index];
        TokenLock storage lock = tokenLocks[_lpToken][lockID];
        require(lockID == _lockID, "lock mismatch");
        require(
            IAMMPairERC20(_lpToken).transfer(_lpToken, lock.amount),
            "fail to send liquidity"
        );
        require(_to != address(0), "to address invalid");
        (uint amount0, uint amount1) = IAMMPairERC20(_lpToken).burn(
            address(this)
        );
        (address token0, address token1) = (
            IAMMPairERC20(_lpToken).token0(),
            IAMMPairERC20(_lpToken).token1()
        );
        (uint256 reAmount0, uint256 reAmount1) = (
            amount0 + lock.remainAmount0,
            amount1 + lock.remainAmount1
        );
        (uint256 reserve0, uint256 reserve1, ) = IAMMPairERC20(_lpToken)
            .getReserves();
        uint256 reAmountOptimal0 = ((lock.k * reserve0) / reserve1).sqrt();
        require(reAmount0 > reAmountOptimal0, "no claimable");
        uint256 reAmountOptimal1 = ((lock.k * reserve1) / reserve0).sqrt();
        require(reAmount1 > reAmountOptimal1, "no claimable");
        TransferHelper.safeApprove(token0, address(router), reAmountOptimal0);
        TransferHelper.safeApprove(token1, address(router), reAmountOptimal1);
        uint256 newAmount0;
        uint256 newAmount1;
        (newAmount0, newAmount1, lock.amount) = router.addLiquidity(
            token0,
            token1,
            reAmountOptimal0,
            reAmountOptimal1,
            0,
            0,
            address(this),
            block.timestamp + 600
        );
        lock.remainAmount0 = reAmountOptimal0 - newAmount0;
        lock.remainAmount1 = reAmountOptimal1 - newAmount1;
        lock.updateTimestamp = block.timestamp;
        uint256 gaimed0 = reAmount0 - reAmountOptimal0;
        uint256 gaimed1 = reAmount1 - reAmountOptimal1;
        if (gaimed0 != 0) {
            TransferHelper.safeTransfer(token0, _to, gaimed0);
        }
        if (gaimed1 != 0) {
            TransferHelper.safeTransfer(token1, _to, gaimed1);
        }
        emit RewardClaimed(token0, token1, gaimed0, gaimed1);
        emit LockUpdated(_lpToken, lock);
    }
}

Other files

  • @openzeppelin/contracts/utils/ReentrancyGuard.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
    
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant NOT_ENTERED = 1;
        uint256 private constant ENTERED = 2;
    
        uint256 private _status;
    
        /**
         * @dev Unauthorized reentrant call.
         */
        error ReentrancyGuardReentrantCall();
    
        constructor() {
            _status = NOT_ENTERED;
        }
    
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and making it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            _nonReentrantBefore();
            _;
            _nonReentrantAfter();
        }
    
        function _nonReentrantBefore() private {
            // On the first call to nonReentrant, _status will be NOT_ENTERED
            if (_status == ENTERED) {
                revert ReentrancyGuardReentrantCall();
            }
    
            // Any calls to nonReentrant after this point will fail
            _status = ENTERED;
        }
    
        function _nonReentrantAfter() private {
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = NOT_ENTERED;
        }
    
        /**
         * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
         * `nonReentrant` function in the call stack.
         */
        function _reentrancyGuardEntered() internal view returns (bool) {
            return _status == ENTERED;
        }
    }
    
  • @openzeppelin/contracts/utils/math/Math.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        /**
         * @dev Muldiv operation overflow.
         */
        error MathOverflowedMulDiv();
    
        enum Rounding {
            Floor, // Toward negative infinity
            Ceil, // Toward positive infinity
            Trunc, // Toward zero
            Expand // Away from zero
        }
    
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                uint256 c = a + b;
                if (c < a) return (false, 0);
                return (true, c);
            }
        }
    
        /**
         * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b > a) return (false, 0);
                return (true, a - b);
            }
        }
    
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                if (a == 0) return (true, 0);
                uint256 c = a * b;
                if (c / a != b) return (false, 0);
                return (true, c);
            }
        }
    
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a / b);
            }
        }
    
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a % b);
            }
        }
    
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a > b ? a : b;
        }
    
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
    
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow.
            return (a & b) + (a ^ b) / 2;
        }
    
        /**
         * @dev Returns the ceiling of the division of two numbers.
         *
         * This differs from standard division with `/` in that it rounds towards infinity instead
         * of rounding towards zero.
         */
        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
            if (b == 0) {
                // Guarantee the same behavior as in a regular Solidity division.
                return a / b;
            }
    
            // (a + b - 1) / b can overflow on addition, so we distribute.
            return a == 0 ? 0 : (a - 1) / b + 1;
        }
    
        /**
         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
         * denominator == 0.
         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
         * Uniswap Labs also under MIT license.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
            unchecked {
                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                // variables such that product = prod1 * 2^256 + prod0.
                uint256 prod0 = x * y; // Least significant 256 bits of the product
                uint256 prod1; // Most significant 256 bits of the product
                assembly {
                    let mm := mulmod(x, y, not(0))
                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                }
    
                // Handle non-overflow cases, 256 by 256 division.
                if (prod1 == 0) {
                    // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                    // The surrounding unchecked block does not change this fact.
                    // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                    return prod0 / denominator;
                }
    
                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                if (denominator <= prod1) {
                    revert MathOverflowedMulDiv();
                }
    
                ///////////////////////////////////////////////
                // 512 by 256 division.
                ///////////////////////////////////////////////
    
                // Make division exact by subtracting the remainder from [prod1 prod0].
                uint256 remainder;
                assembly {
                    // Compute remainder using mulmod.
                    remainder := mulmod(x, y, denominator)
    
                    // Subtract 256 bit number from 512 bit number.
                    prod1 := sub(prod1, gt(remainder, prod0))
                    prod0 := sub(prod0, remainder)
                }
    
                // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
                // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
    
                uint256 twos = denominator & (0 - denominator);
                assembly {
                    // Divide denominator by twos.
                    denominator := div(denominator, twos)
    
                    // Divide [prod1 prod0] by twos.
                    prod0 := div(prod0, twos)
    
                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                    twos := add(div(sub(0, twos), twos), 1)
                }
    
                // Shift in bits from prod1 into prod0.
                prod0 |= prod1 * twos;
    
                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                // four bits. That is, denominator * inv = 1 mod 2^4.
                uint256 inverse = (3 * denominator) ^ 2;
    
                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
                // works in modular arithmetic, doubling the correct bits in each step.
                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                inverse *= 2 - denominator * inverse; // inverse mod 2^256
    
                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                // is no longer required.
                result = prod0 * inverse;
                return result;
            }
        }
    
        /**
         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
            uint256 result = mulDiv(x, y, denominator);
            if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
                result += 1;
            }
            return result;
        }
    
        /**
         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
         * towards zero.
         *
         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
         */
        function sqrt(uint256 a) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
    
            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
            //
            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
            //
            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
            //
            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
            uint256 result = 1 << (log2(a) >> 1);
    
            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
            // into the expected uint128 result.
            unchecked {
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                return min(result, a / result);
            }
        }
    
        /**
         * @notice Calculates sqrt(a), following the selected rounding direction.
         */
        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = sqrt(a);
                return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
            }
        }
    
        /**
         * @dev Return the log in base 2 of a positive value rounded towards zero.
         * Returns 0 if given 0.
         */
        function log2(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 128;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 64;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 32;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 16;
                }
                if (value >> 8 > 0) {
                    value >>= 8;
                    result += 8;
                }
                if (value >> 4 > 0) {
                    value >>= 4;
                    result += 4;
                }
                if (value >> 2 > 0) {
                    value >>= 2;
                    result += 2;
                }
                if (value >> 1 > 0) {
                    result += 1;
                }
            }
            return result;
        }
    
        /**
         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log2(value);
                return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
            }
        }
    
        /**
         * @dev Return the log in base 10 of a positive value rounded towards zero.
         * Returns 0 if given 0.
         */
        function log10(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >= 10 ** 64) {
                    value /= 10 ** 64;
                    result += 64;
                }
                if (value >= 10 ** 32) {
                    value /= 10 ** 32;
                    result += 32;
                }
                if (value >= 10 ** 16) {
                    value /= 10 ** 16;
                    result += 16;
                }
                if (value >= 10 ** 8) {
                    value /= 10 ** 8;
                    result += 8;
                }
                if (value >= 10 ** 4) {
                    value /= 10 ** 4;
                    result += 4;
                }
                if (value >= 10 ** 2) {
                    value /= 10 ** 2;
                    result += 2;
                }
                if (value >= 10 ** 1) {
                    result += 1;
                }
            }
            return result;
        }
    
        /**
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log10(value);
                return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
            }
        }
    
        /**
         * @dev Return the log in base 256 of a positive value rounded towards zero.
         * Returns 0 if given 0.
         *
         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
         */
        function log256(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 16;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 8;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 4;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 2;
                }
                if (value >> 8 > 0) {
                    result += 1;
                }
            }
            return result;
        }
    
        /**
         * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log256(value);
                return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
            }
        }
    
        /**
         * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
         */
        function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
            return uint8(rounding) % 2 == 1;
        }
    }
    
  • @openzeppelin/contracts/utils/structs/EnumerableSet.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
    // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Library for managing
     * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
     * types.
     *
     * Sets have the following properties:
     *
     * - Elements are added, removed, and checked for existence in constant time
     * (O(1)).
     * - Elements are enumerated in O(n). No guarantees are made on the ordering.
     *
     * ```solidity
     * contract Example {
     *     // Add the library methods
     *     using EnumerableSet for EnumerableSet.AddressSet;
     *
     *     // Declare a set state variable
     *     EnumerableSet.AddressSet private mySet;
     * }
     * ```
     *
     * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
     * and `uint256` (`UintSet`) are supported.
     *
     * [WARNING]
     * ====
     * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
     * unusable.
     * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
     *
     * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
     * array of EnumerableSet.
     * ====
     */
    library EnumerableSet {
        // To implement this library for multiple types with as little code
        // repetition as possible, we write it in terms of a generic Set type with
        // bytes32 values.
        // The Set implementation uses private functions, and user-facing
        // implementations (such as AddressSet) are just wrappers around the
        // underlying Set.
        // This means that we can only create new EnumerableSets for types that fit
        // in bytes32.
    
        struct Set {
            // Storage of set values
            bytes32[] _values;
            // Position is the index of the value in the `values` array plus 1.
            // Position 0 is used to mean a value is not in the set.
            mapping(bytes32 value => uint256) _positions;
        }
    
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function _add(Set storage set, bytes32 value) private returns (bool) {
            if (!_contains(set, value)) {
                set._values.push(value);
                // The value is stored at length-1, but we add 1 to all indexes
                // and use 0 as a sentinel value
                set._positions[value] = set._values.length;
                return true;
            } else {
                return false;
            }
        }
    
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function _remove(Set storage set, bytes32 value) private returns (bool) {
            // We cache the value's position to prevent multiple reads from the same storage slot
            uint256 position = set._positions[value];
    
            if (position != 0) {
                // Equivalent to contains(set, value)
                // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                // the array, and then remove the last element (sometimes called as 'swap and pop').
                // This modifies the order of the array, as noted in {at}.
    
                uint256 valueIndex = position - 1;
                uint256 lastIndex = set._values.length - 1;
    
                if (valueIndex != lastIndex) {
                    bytes32 lastValue = set._values[lastIndex];
    
                    // Move the lastValue to the index where the value to delete is
                    set._values[valueIndex] = lastValue;
                    // Update the tracked position of the lastValue (that was just moved)
                    set._positions[lastValue] = position;
                }
    
                // Delete the slot where the moved value was stored
                set._values.pop();
    
                // Delete the tracked position for the deleted slot
                delete set._positions[value];
    
                return true;
            } else {
                return false;
            }
        }
    
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function _contains(Set storage set, bytes32 value) private view returns (bool) {
            return set._positions[value] != 0;
        }
    
        /**
         * @dev Returns the number of values on the set. O(1).
         */
        function _length(Set storage set) private view returns (uint256) {
            return set._values.length;
        }
    
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function _at(Set storage set, uint256 index) private view returns (bytes32) {
            return set._values[index];
        }
    
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function _values(Set storage set) private view returns (bytes32[] memory) {
            return set._values;
        }
    
        // Bytes32Set
    
        struct Bytes32Set {
            Set _inner;
        }
    
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _add(set._inner, value);
        }
    
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _remove(set._inner, value);
        }
    
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
            return _contains(set._inner, value);
        }
    
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(Bytes32Set storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
    
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
            return _at(set._inner, index);
        }
    
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
            bytes32[] memory store = _values(set._inner);
            bytes32[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    
        // AddressSet
    
        struct AddressSet {
            Set _inner;
        }
    
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(AddressSet storage set, address value) internal returns (bool) {
            return _add(set._inner, bytes32(uint256(uint160(value))));
        }
    
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(AddressSet storage set, address value) internal returns (bool) {
            return _remove(set._inner, bytes32(uint256(uint160(value))));
        }
    
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(AddressSet storage set, address value) internal view returns (bool) {
            return _contains(set._inner, bytes32(uint256(uint160(value))));
        }
    
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(AddressSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
    
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(AddressSet storage set, uint256 index) internal view returns (address) {
            return address(uint160(uint256(_at(set._inner, index))));
        }
    
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(AddressSet storage set) internal view returns (address[] memory) {
            bytes32[] memory store = _values(set._inner);
            address[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    
        // UintSet
    
        struct UintSet {
            Set _inner;
        }
    
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(UintSet storage set, uint256 value) internal returns (bool) {
            return _add(set._inner, bytes32(value));
        }
    
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(UintSet storage set, uint256 value) internal returns (bool) {
            return _remove(set._inner, bytes32(value));
        }
    
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(UintSet storage set, uint256 value) internal view returns (bool) {
            return _contains(set._inner, bytes32(value));
        }
    
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(UintSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
    
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(UintSet storage set, uint256 index) internal view returns (uint256) {
            return uint256(_at(set._inner, index));
        }
    
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(UintSet storage set) internal view returns (uint256[] memory) {
            bytes32[] memory store = _values(set._inner);
            uint256[] memory result;
    
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
    
            return result;
        }
    }
    
  • contracts/interfaces/IAMMFactory.sol
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity ^0.8.24;
    
    interface IAMMFactory {
    	event PairCreated(
    		address indexed token0,
    		address indexed token1,
    		address pair,
    		uint
    	);
    	function getPair(
    		address tokenA,
    		address tokenB
    	) external view returns (address pair);
    	function createPair(
    		address tokenA,
    		address tokenB
    	) external returns (address pair);
    	function allPairs(uint) external view returns (address pair);
    	function allPairsLength() external view returns (uint);
    }
    
  • contracts/interfaces/IAMMPairERC20.sol
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity ^0.8.24;
    
    interface IAMMPairERC20 {
    	event Approval(address indexed owner, address indexed spender, uint value);
        event Transfer(address indexed from, address indexed to, uint value);
    
        function name() external pure returns (string memory);
        function symbol() external pure returns (string memory);
        function decimals() external pure returns (uint8);
        function totalSupply() external view returns (uint);
        function balanceOf(address owner) external view returns (uint);
        function allowance(address owner, address spender) external view returns (uint);
    
        function approve(address spender, uint value) external returns (bool);
        function transfer(address to, uint value) external returns (bool);
        function transferFrom(address from, address to, uint value) external returns (bool);
    
        function DOMAIN_SEPARATOR() external view returns (bytes32);
        function PERMIT_TYPEHASH() external pure returns (bytes32);
        function nonces(address owner) external view returns (uint);
    
        function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
    
        event Mint(address indexed sender, uint amount0, uint amount1);
        event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
        event Swap(
            address indexed sender,
            uint amount0In,
            uint amount1In,
            uint amount0Out,
            uint amount1Out,
            address indexed to
        );
        event Sync(uint112 reserve0, uint112 reserve1);
    
        function MINIMUM_LIQUIDITY() external pure returns (uint);
        function factory() external view returns (address);
        function token0() external view returns (address);
        function token1() external view returns (address);
        function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
        function price0CumulativeLast() external view returns (uint);
        function price1CumulativeLast() external view returns (uint);
        function kLast() external view returns (uint);
    
        function mint(address to) external returns (uint liquidity);
        function burn(address to) external returns (uint amount0, uint amount1);
        function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
        function skim(address to) external;
        function sync() external;
    
        function initialize(address, address) external;
    }
    
  • contracts/interfaces/IAMMRouter.sol
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity >=0.8.0;
    
    interface IAMMRouter {
        function addLiquidity(
            address tokenA,
            address tokenB,
            uint amountADesired,
            uint amountBDesired,
            uint amountAMin,
            uint amountBMin,
            address to,
            uint deadline
        ) external returns (uint amountA, uint amountB, uint liquidity);
        function addLiquidityETH(
            address token,            
            uint amountTokenDesired,  
            uint amountTokenMin,     
            uint amountETHMin,        
            address to,               
            uint deadline            
        ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    }
  • contracts/interfaces/IMigrationLocker.sol
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity >=0.8.20;
    
    interface IMigrationLocker {
        struct TokenLock {
            uint256 lockDate;
            uint256 unLockDate;
            uint256 amount; // the current amount of liquidity position
            uint256 remainAmount0; // the remaining amount of token0
            uint256 remainAmount1; // the remaining amount of token1
            uint256 k; // the snapshot k of the lp token
            uint256 lockID;
            address owner;
            uint256 updateTimestamp;
        }
    
        event Deposit(
            address indexed lpToken,
            address indexed user,
            uint256 amount,
            TokenLock lock
        );
        event Withdraw(
            address indexed lpToken,
            address indexed user,
            uint256 amount,
            TokenLock lock
        );
        event LockUpdated(address indexed lpToken, TokenLock lock);
        event RewardClaimed(address indexed token0, address indexed token1, uint256 amount0, uint256 amount1);
    
        function lockLPToken(
            address lpToken,
            address withdrawer,
            uint256 amount,
            uint256 unlockDate
        ) external;
    
        function claimeLpReward(
            address _lpToken,
            uint256 _index,
            uint256 _lockID,
            address _to
        ) external;
    
    		function allLockIDs(
            address _lpToken
        ) external view returns (uint256[] memory lockIDs);
    }
    
  • contracts/libraries/TransferHelper.sol
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity ^0.8.24;
    
    // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
    library TransferHelper {
    	function safeApprove(address token, address to, uint value) internal {
    		// bytes4(keccak256(bytes('approve(address,uint256)')));
    		(bool success, bytes memory data) = token.call(
    			abi.encodeWithSelector(0x095ea7b3, to, value)
    		);
    		require(
    			success && (data.length == 0 || abi.decode(data, (bool))),
    			"APPROVE_FAILED"
    		);
    	}
    
    	function safeTransfer(address token, address to, uint value) internal {
    		// bytes4(keccak256(bytes('transfer(address,uint256)')));
    		(bool success, bytes memory data) = token.call(
    			abi.encodeWithSelector(0xa9059cbb, to, value)
    		);
    		require(
    			success && (data.length == 0 || abi.decode(data, (bool))),
    			"TRANSFER_FAILED"
    		);
    	}
    
    	function safeTransferFrom(
    		address token,
    		address from,
    		address to,
    		uint value
    	) internal {
    		// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
    		(bool success, bytes memory data) = token.call(
    			abi.encodeWithSelector(0x23b872dd, from, to, value)
    		);
    		require(
    			success && (data.length == 0 || abi.decode(data, (bool))),
    			"TRANSFER_FROM_FAILED"
    		);
    	}
    
    	function safeTransferETH(address to, uint value) internal {
    		(bool success, ) = to.call{value: value}(new bytes(0));
    		require(success, "ETH_TRANSFER_FAILED");
    	}
    }
    
ABI · 15 entries
json
[
  {
    "type": "constructor",
    "inputs": [
      {
        "name": "_factory",
        "type": "address",
        "internalType": "contract IAMMFactory"
      },
      {
        "name": "_router",
        "type": "address",
        "internalType": "contract IAMMRouter"
      },
      {
        "name": "_migrator",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "ReentrancyGuardReentrantCall",
    "type": "error",
    "inputs": []
  },
  {
    "name": "Deposit",
    "type": "event",
    "inputs": [
      {
        "name": "lpToken",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "lock",
        "type": "tuple",
        "indexed": false,
        "components": [
          {
            "name": "lockDate",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "unLockDate",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "amount",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "remainAmount0",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "remainAmount1",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "k",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "lockID",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "owner",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "updateTimestamp",
            "type": "uint256",
            "internalType": "uint256"
          }
        ],
        "internalType": "struct IMigrationLocker.TokenLock"
      }
    ],
    "anonymous": false
  },
  {
    "name": "LockUpdated",
    "type": "event",
    "inputs": [
      {
        "name": "lpToken",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "lock",
        "type": "tuple",
        "indexed": false,
        "components": [
          {
            "name": "lockDate",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "unLockDate",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "amount",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "remainAmount0",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "remainAmount1",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "k",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "lockID",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "owner",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "updateTimestamp",
            "type": "uint256",
            "internalType": "uint256"
          }
        ],
        "internalType": "struct IMigrationLocker.TokenLock"
      }
    ],
    "anonymous": false
  },
  {
    "name": "RewardClaimed",
    "type": "event",
    "inputs": [
      {
        "name": "token0",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "token1",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "amount0",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "amount1",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "Withdraw",
    "type": "event",
    "inputs": [
      {
        "name": "lpToken",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "lock",
        "type": "tuple",
        "indexed": false,
        "components": [
          {
            "name": "lockDate",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "unLockDate",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "amount",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "remainAmount0",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "remainAmount1",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "k",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "lockID",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "owner",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "updateTimestamp",
            "type": "uint256",
            "internalType": "uint256"
          }
        ],
        "internalType": "struct IMigrationLocker.TokenLock"
      }
    ],
    "anonymous": false
  },
  {
    "name": "allLockIDs",
    "type": "function",
    "inputs": [
      {
        "name": "_lpToken",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "lockIDs",
        "type": "uint256[]",
        "internalType": "uint256[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "allLockedTokens",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "lpTokens",
        "type": "address[]",
        "internalType": "address[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "claimeLpReward",
    "type": "function",
    "inputs": [
      {
        "name": "_lpToken",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_index",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_lockID",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_to",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "factory",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "contract IAMMFactory"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "lockLPToken",
    "type": "function",
    "inputs": [
      {
        "name": "_lpToken",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_withdrawer",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_amount",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_lockSeconds",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "migrator",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "router",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "contract IAMMRouter"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "tokenLocks",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "lockDate",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "unLockDate",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "remainAmount0",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "remainAmount1",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "k",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "lockID",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "owner",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "updateTimestamp",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "withdraw",
    "type": "function",
    "inputs": [
      {
        "name": "_lpToken",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_index",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_lockID",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  }
]