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"
}
]