Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x542a28dA47c4b7148EcE0bb86497f033b8037ED6
Balance 0 ETH
Nonce 1
Code Size 7963 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

7963 bytes
0x608060405234801561001057600080fd5b506004361061010b5760003560e01c80638ee78244116100a2578063b425f80211610071578063b425f80214610260578063bdac093b14610293578063c168fe0f146102af578063c3f9ab58146102cd578063ed0bf8e1146102fd5761010b565b80638ee78244146101d6578063953c1659146101f45780639c3ee24414610212578063afb593b5146102425761010b565b80634e71d92d116100de5780634e71d92d14610172578063535fd2c51461017c57806361d027b31461019a57806377137b3a146101b85761010b565b80630923eb501461011057806318b0ea651461012c5780632dea9ef71461014a578063386bfc9814610154575b600080fd5b61012a600480360381019061012591906116b1565b61031b565b005b610134610548565b604051610141919061175d565b60405180910390f35b61015261056c565b005b61015c61087d565b6040516101699190611791565b60405180910390f35b61017a6108a1565b005b6101846108ac565b60405161019191906117bb565b60405180910390f35b6101a261096d565b6040516101af91906117f7565b60405180910390f35b6101c0610991565b6040516101cd919061175d565b60405180910390f35b6101de6109b5565b6040516101eb919061182d565b60405180910390f35b6101fc6109e5565b6040516102099190611867565b60405180910390f35b61022c600480360381019061022791906118ae565b610a09565b60405161023991906117bb565b60405180910390f35b61024a610b29565b60405161025791906117bb565b60405180910390f35b61027a600480360381019061027591906118db565b610b2f565b60405161028a949392919061191b565b60405180910390f35b6102ad60048036038101906102a891906119ea565b610bac565b005b6102b7611096565b6040516102c491906117bb565b60405180910390f35b6102e760048036038101906102e291906118ae565b6110ba565b6040516102f49190611b80565b60405180910390f35b6103056111d3565b604051610312919061175d565b60405180910390f35b7f000000000000000000000000000000000000000000000000000000006901b44163ffffffff1642111561037b576040517ff2c81fad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000007d29a64504629172a429e64183d6673b9dacbfce73ffffffffffffffffffffffffffffffffffffffff166323b872dd3330846040518463ffffffff1660e01b81526004016103d893929190611ba2565b6020604051808303816000875af11580156103f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061041b9190611c05565b507f000000000000000000000000b7b37b81d4497ab317fc9d4a370cf243043d6bbe73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33836040518363ffffffff1660e01b8152600401610477929190611c32565b6020604051808303816000875af1158015610496573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104ba9190611c05565b507f0000000000000000000000007d29a64504629172a429e64183d6673b9dacbfce73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f5c03ddec0f8a6691ba22930d9e659251d6557d8f4633e66a7ead825ce19f2f658384600060405161053d93929190611cd2565b60405180910390a350565b7f0000000000000000000000009b5c38cc2d1ba05ed87c8f8a2418475bacb2007381565b7f000000000000000000000000000000000000000000000000000000006901b44163ffffffff1642116105cb576040517f1f461c0800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000b7b37b81d4497ab317fc9d4a370cf243043d6bbe73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb7f00000000000000000000000059a39e876ddcba28eb4fd12e582be960a34a3d537f000000000000000000000000b7b37b81d4497ab317fc9d4a370cf243043d6bbe73ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161068191906117f7565b602060405180830381865afa15801561069e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c29190611d1e565b6040518363ffffffff1660e01b81526004016106df929190611c32565b6020604051808303816000875af11580156106fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107229190611c05565b507f0000000000000000000000009b5c38cc2d1ba05ed87c8f8a2418475bacb2007373ffffffffffffffffffffffffffffffffffffffff1663a9059cbb7f00000000000000000000000059a39e876ddcba28eb4fd12e582be960a34a3d537f0000000000000000000000009b5c38cc2d1ba05ed87c8f8a2418475bacb2007373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016107d991906117f7565b602060405180830381865afa1580156107f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061081a9190611d1e565b6040518363ffffffff1660e01b8152600401610837929190611c32565b6020604051808303816000875af1158015610856573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061087a9190611c05565b50565b7ff9282e6838014725915abb0079cf92874fb94a6a0a1d15e3232182825b0b266f81565b6108aa336111f7565b565b60007f000000000000000000000000000000000000000000108b2a2c280290940000007f0000000000000000000000007d29a64504629172a429e64183d6673b9dacbfce73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561093a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061095e9190611d1e565b6109689190611d7a565b905090565b7f00000000000000000000000059a39e876ddcba28eb4fd12e582be960a34a3d5381565b7f0000000000000000000000007d29a64504629172a429e64183d6673b9dacbfce81565b60007f000000000000000000000000000000000000000000000000000000006901b44163ffffffff164211905090565b7f000000000000000000000000000000000000000000000000000000006901b44181565b60007f000000000000000000000000000000000000000000000000000000006901b44163ffffffff16421115610a425760009050610b24565b60008060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060005b8180549050811015610b21576000828281548110610aa857610aa7611dae565b5b90600052602060002090600202019050428160010160049054906101000a900463ffffffff1663ffffffff16108015610afb575060008160010160089054906101000a900463ffffffff1663ffffffff16145b15610b1357806000015484610b109190611d7a565b93505b508080600101915050610a87565b50505b919050565b60015481565b60006020528160005260406000208181548110610b4b57600080fd5b9060005260206000209060020201600091509150508060000154908060010160009054906101000a900463ffffffff16908060010160049054906101000a900463ffffffff16908060010160089054906101000a900463ffffffff16905084565b7f000000000000000000000000000000000000000000000000000000006901b44163ffffffff16421115610c0c576040517ff2c81fad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600033604051602001610c1f91906117f7565b60405160208183030381529060405280519060200120604051602001610c459190611dfe565b604051602081830303815290604052805190602001209050610c8983837ff9282e6838014725915abb0079cf92874fb94a6a0a1d15e3232182825b0b266f84611441565b610cbf576040517f079359e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460016000828254610cd19190611d7a565b925050819055507f000000000000000000000000000000000000000000108b2a2c280290940000006001541115610d34576040517f393de1d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000009b5c38cc2d1ba05ed87c8f8a2418475bacb2007373ffffffffffffffffffffffffffffffffffffffff166323b872dd3330886040518463ffffffff1660e01b8152600401610d9193929190611ba2565b6020604051808303816000875af1158015610db0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd49190611c05565b506000610de1868661145a565b90506000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190806001815401808255809150506001900390600052602060002090600202016000909190919091506000820151816000015560208201518160010160006101000a81548163ffffffff021916908363ffffffff16021790555060408201518160010160046101000a81548163ffffffff021916908363ffffffff16021790555060608201518160010160086101000a81548163ffffffff021916908363ffffffff16021790555050507f0000000000000000000000009b5c38cc2d1ba05ed87c8f8a2418475bacb2007373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f5c03ddec0f8a6691ba22930d9e659251d6557d8f4633e66a7ead825ce19f2f6588846000015189604051610f5193929190611cd2565b60405180910390a36000816000015187610f6b9190611e19565b90506000811115611084577f000000000000000000000000b7b37b81d4497ab317fc9d4a370cf243043d6bbe73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb7f00000000000000000000000059a39e876ddcba28eb4fd12e582be960a34a3d53836040518363ffffffff1660e01b8152600401610ff1929190611c32565b6020604051808303816000875af1158015611010573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110349190611c05565b503373ffffffffffffffffffffffffffffffffffffffff167f8bf1eb36cb5fffab66d982aa79a39e9ea5eb93dd4321e10a07f5d1a1cf611e278260405161107b91906117bb565b60405180910390a25b61108d336111f7565b50505050505050565b7f000000000000000000000000000000000000000000108b2a2c2802909400000081565b60606000808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b828210156111c85783829060005260206000209060020201604051806080016040529081600082015481526020016001820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016001820160049054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016001820160089054906101000a900463ffffffff1663ffffffff1663ffffffff16815250508152602001906001019061111a565b505050509050919050565b7f000000000000000000000000b7b37b81d4497ab317fc9d4a370cf243043d6bbe81565b7f000000000000000000000000000000000000000000000000000000006901b44163ffffffff16421115611257576040517ff2c81fad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060005b818054905081101561143c5760008282815481106112bd576112bc611dae565b5b90600052602060002090600202019050428160010160049054906101000a900463ffffffff1663ffffffff16108015611310575060008160010160089054906101000a900463ffffffff1663ffffffff16145b1561142e57428160010160086101000a81548163ffffffff021916908363ffffffff1602179055507f000000000000000000000000b7b37b81d4497ab317fc9d4a370cf243043d6bbe73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb8583600001546040518363ffffffff1660e01b8152600401611397929190611c32565b6020604051808303816000875af11580156113b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113da9190611c05565b508373ffffffffffffffffffffffffffffffffffffffff167fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a826000015460405161142591906117bb565b60405180910390a25b50808060010191505061129c565b505050565b60008261144f8686856115a3565b149050949350505050565b611462611637565b61146a611637565b42816020019063ffffffff16908163ffffffff16815250506000816060019063ffffffff16908163ffffffff16815250506002808111156114ae576114ad611c5b565b5b8360028111156114c1576114c0611c5b565b5b036114fb576276a700426114d59190611e4d565b816040019063ffffffff16908163ffffffff168152505083816000018181525050611599565b6001600281111561150f5761150e611c5b565b5b83600281111561152257611521611c5b565b5b036115685762278d00426115369190611e4d565b816040019063ffffffff16908163ffffffff168152505060028461155a9190611eb4565b816000018181525050611598565b6000816040019063ffffffff16908163ffffffff168152505060038461158e9190611eb4565b8160000181815250505b5b8091505092915050565b60008082905060005b858590508110156115e9576115da828787848181106115ce576115cd611dae565b5b905060200201356115f5565b915080806001019150506115ac565b50809150509392505050565b600081831061160d576116088284611620565b611618565b6116178383611620565b5b905092915050565b600082600052816020526040600020905092915050565b604051806080016040528060008152602001600063ffffffff168152602001600063ffffffff168152602001600063ffffffff1681525090565b600080fd5b600080fd5b6000819050919050565b61168e8161167b565b811461169957600080fd5b50565b6000813590506116ab81611685565b92915050565b6000602082840312156116c7576116c6611671565b5b60006116d58482850161169c565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061172361171e611719846116de565b6116fe565b6116de565b9050919050565b600061173582611708565b9050919050565b60006117478261172a565b9050919050565b6117578161173c565b82525050565b6000602082019050611772600083018461174e565b92915050565b6000819050919050565b61178b81611778565b82525050565b60006020820190506117a66000830184611782565b92915050565b6117b58161167b565b82525050565b60006020820190506117d060008301846117ac565b92915050565b60006117e1826116de565b9050919050565b6117f1816117d6565b82525050565b600060208201905061180c60008301846117e8565b92915050565b60008115159050919050565b61182781611812565b82525050565b6000602082019050611842600083018461181e565b92915050565b600063ffffffff82169050919050565b61186181611848565b82525050565b600060208201905061187c6000830184611858565b92915050565b61188b816117d6565b811461189657600080fd5b50565b6000813590506118a881611882565b92915050565b6000602082840312156118c4576118c3611671565b5b60006118d284828501611899565b91505092915050565b600080604083850312156118f2576118f1611671565b5b600061190085828601611899565b92505060206119118582860161169c565b9150509250929050565b600060808201905061193060008301876117ac565b61193d6020830186611858565b61194a6040830185611858565b6119576060830184611858565b95945050505050565b6003811061196d57600080fd5b50565b60008135905061197f81611960565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126119aa576119a9611985565b5b8235905067ffffffffffffffff8111156119c7576119c661198a565b5b6020830191508360208202830111156119e3576119e261198f565b5b9250929050565b60008060008060608587031215611a0457611a03611671565b5b6000611a128782880161169c565b9450506020611a2387828801611970565b935050604085013567ffffffffffffffff811115611a4457611a43611676565b5b611a5087828801611994565b925092505092959194509250565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b611a938161167b565b82525050565b611aa281611848565b82525050565b608082016000820151611abe6000850182611a8a565b506020820151611ad16020850182611a99565b506040820151611ae46040850182611a99565b506060820151611af76060850182611a99565b50505050565b6000611b098383611aa8565b60808301905092915050565b6000602082019050919050565b6000611b2d82611a5e565b611b378185611a69565b9350611b4283611a7a565b8060005b83811015611b73578151611b5a8882611afd565b9750611b6583611b15565b925050600181019050611b46565b5085935050505092915050565b60006020820190508181036000830152611b9a8184611b22565b905092915050565b6000606082019050611bb760008301866117e8565b611bc460208301856117e8565b611bd160408301846117ac565b949350505050565b611be281611812565b8114611bed57600080fd5b50565b600081519050611bff81611bd9565b92915050565b600060208284031215611c1b57611c1a611671565b5b6000611c2984828501611bf0565b91505092915050565b6000604082019050611c4760008301856117e8565b611c5460208301846117ac565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110611c9b57611c9a611c5b565b5b50565b6000819050611cac82611c8a565b919050565b6000611cbc82611c9e565b9050919050565b611ccc81611cb1565b82525050565b6000606082019050611ce760008301866117ac565b611cf460208301856117ac565b611d016040830184611cc3565b949350505050565b600081519050611d1881611685565b92915050565b600060208284031215611d3457611d33611671565b5b6000611d4284828501611d09565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000611d858261167b565b9150611d908361167b565b9250828201905080821115611da857611da7611d4b565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000819050919050565b611df8611df382611778565b611ddd565b82525050565b6000611e0a8284611de7565b60208201915081905092915050565b6000611e248261167b565b9150611e2f8361167b565b9250828203905081811115611e4757611e46611d4b565b5b92915050565b6000611e5882611848565b9150611e6383611848565b9250828201905063ffffffff811115611e7f57611e7e611d4b565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000611ebf8261167b565b9150611eca8361167b565b925082611eda57611ed9611e85565b5b82820490509291505056fea2646970667358221220eb12c6fcfbe0e6ef76f477eec98573e556bfeaacb7d5d6195868118ab59a4f8964736f6c63430008180033

Verified Source Code Full Match

Compiler: v0.8.24+commit.e11b9ed9 EVM: paris Optimization: No
Migrator.sol 226 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

// Migrator is a contract for migrating VXV and SBIO tokens to VAIX.
//
// VXV tokens migrate 1:1 for VAIX.
//
// SBIO tokens migrate with an optional lock period that increases receipt amount.
contract Migrator {
    // The VAIX that is locked as part of an SBIO migration.
    struct LockedMigration {
        uint256 receiptAmount; // how much VAIX will be received
        uint32 lockedAt; // block.timestamp of the migration call
        uint32 unlockAfter; // will be 0 when the lock duration is None (immediate)
        uint32 claimedAt; // will be 0 until it is claimed after unlocking
    }

    enum LockDuration {
        None, // migrate 3:1 for VAIX, immediately
        OneMonth, // migrate 2:1 for VAIX, locked for 30 days
        ThreeMonths // migrate 1:1 for VAIX, locked for 90 days
    }

    IERC20 public immutable VAIX;
    IERC20 public immutable VXV;
    IERC20 public immutable SBIO;
    address public immutable treasury;
    mapping(address => LockedMigration[]) public locks;
    bytes32 public immutable whitelistRoot;
    uint32 public immutable migrationClosesAfter;
    uint256 public immutable sbioMigrationCap;
    uint256 public sbioMigrationTotal;

    error WhitelistNotVerified();
    error MigrationWindowOpen();
    error MigrationWindowClosed();
    error MigrationCapExceeded();

    // When a user has migrated tokens (VXV or SBIO) for VAIX tokens.
    event Migrated(
        address indexed user,
        address indexed depositToken, // VXV or SBIO
        uint256 depositAmount,
        uint256 receiptAmount,
        LockDuration lockDuration
    );

    // When a user claims VAIX tokens that were previously locked.
    event Claimed(address indexed user, uint256 receiptAmount);

    // When the treasury receives VAIX because an SBIO holder opted for a shorter migration lock.
    // e.g. when they opt for no lock then 2/3 of the VAIX is sent to the treasury instead.
    event TreasuryReceipt(address indexed user, uint256 treasuryReceiptAmount);

    constructor(
        address treasuryAddress,
        address vaixAddress,
        address vxvAddress,
        address sbioAddress,
        bytes32 sbioWhitelistRoot,
        uint256 _sbioMigrationCap,
        uint32 _migrationClosesAfter
    ) {
        treasury = treasuryAddress;
        VAIX = IERC20(vaixAddress);
        VXV = IERC20(vxvAddress);
        SBIO = IERC20(sbioAddress);
        sbioMigrationCap = _sbioMigrationCap;
        sbioMigrationTotal = 0;
        whitelistRoot = sbioWhitelistRoot;
        migrationClosesAfter = _migrationClosesAfter;
    }

    // Returns the maximum VAIX required to perform all migrations.
    function maximumMigrationSupply() external view returns (uint256) {
        return VXV.totalSupply() + sbioMigrationCap;
    }

    // Migrate VXV tokens to VAIX.
    function migrateFromVXV(uint256 amount) external {
        if (block.timestamp > migrationClosesAfter) {
            revert MigrationWindowClosed();
        }
        VXV.transferFrom(msg.sender, address(this), amount);
        VAIX.transfer(msg.sender, amount);
        emit Migrated(
            msg.sender,
            address(VXV),
            amount,
            amount,
            LockDuration.None
        );
    }

    // Migrate SBIO tokens to VAIX.
    // The receipt amount is determined by the lock duration.
    // When `LockDuration.None` then SBIO migrates 3:1 for VAIX immediately.
    // When `LockDuration.OneMonth` then SBIO migrates 2:1 for VAIX after 30 days.
    // When `LockDuration.ThreeMonths` then SBIO migrates 1:1 for VAIX after 90 days.
    // The remainder from accelerating the migration is sent to the treasury.
    //
    // After the lock period, a user will have a `.claimableBalanceOf` and can `.claim` their VAIX.
    function migrateFromSBIO(
        uint256 amount,
        LockDuration lockDuration,
        bytes32[] calldata whitelistProof
    ) external {
        if (block.timestamp > migrationClosesAfter) {
            revert MigrationWindowClosed();
        }
        bytes32 whitelistLeaf = keccak256(
            bytes.concat(keccak256(abi.encode(msg.sender)))
        );
        if (
            !MerkleProof.verifyCalldata(
                whitelistProof,
                whitelistRoot,
                whitelistLeaf
            )
        ) {
            revert WhitelistNotVerified();
        }
        sbioMigrationTotal += amount;
        if (sbioMigrationTotal > sbioMigrationCap) {
            revert MigrationCapExceeded();
        }
        SBIO.transferFrom(msg.sender, address(this), amount);
        LockedMigration memory lock = _makeSBIOLock(amount, lockDuration);
        locks[msg.sender].push(lock);
        emit Migrated(
            msg.sender,
            address(SBIO),
            amount,
            lock.receiptAmount,
            lockDuration
        );
        uint256 remainder = amount - lock.receiptAmount;
        if (remainder > 0) {
            VAIX.transfer(treasury, remainder);
            emit TreasuryReceipt(msg.sender, remainder);
        }
        _claimUnlockedMigrations(msg.sender);
    }

    // Returns all locks for a `user`.
    function locksOf(
        address user
    ) external view returns (LockedMigration[] memory) {
        return locks[user];
    }

    // Claim unlocked VAIX tokens.
    // See `.claimableBalanceOf()` to determine the VAIX that will be claimed.
    // Note: this will claim VAIX tokens across any expired locks for the `msg.sender`.
    function claim() external {
        _claimUnlockedMigrations(msg.sender);
    }

    // Returns the VAIX that a `user` can `.claim()` now.
    function claimableBalanceOf(
        address user
    ) external view returns (uint256 balance) {
        balance = 0;
        if (block.timestamp > migrationClosesAfter) {
            return 0;
        }
        LockedMigration[] storage userLocks = locks[user];
        for (uint256 i = 0; i < userLocks.length; i++) {
            LockedMigration storage lock = userLocks[i];
            if (lock.unlockAfter < block.timestamp && lock.claimedAt == 0) {
                balance += lock.receiptAmount;
            }
        }
    }

    function withdrawAfterClosing() external {
        if (block.timestamp <= migrationClosesAfter) {
            revert MigrationWindowOpen();
        }
        VAIX.transfer(treasury, VAIX.balanceOf(address(this)));
        SBIO.transfer(treasury, SBIO.balanceOf(address(this)));
    }

    function isMigrationClosed() external view returns (bool) {
        return block.timestamp > migrationClosesAfter;
    }

    function _claimUnlockedMigrations(address user) internal {
        if (block.timestamp > migrationClosesAfter) {
            revert MigrationWindowClosed();
        }
        LockedMigration[] storage userLocks = locks[user];
        for (uint256 i = 0; i < userLocks.length; i++) {
            LockedMigration storage lock = userLocks[i];
            if (lock.unlockAfter < block.timestamp && lock.claimedAt == 0) {
                lock.claimedAt = uint32(block.timestamp);
                VAIX.transfer(user, lock.receiptAmount);
                emit Claimed(user, lock.receiptAmount);
            }
        }
    }

    function _makeSBIOLock(
        uint256 amount,
        LockDuration duration
    ) private view returns (LockedMigration memory) {
        LockedMigration memory lock;
        lock.lockedAt = uint32(block.timestamp);
        lock.claimedAt = 0;
        if (duration == LockDuration.ThreeMonths) {
            lock.unlockAfter = uint32(block.timestamp) + 90 days;
            lock.receiptAmount = amount;
        } else if (duration == LockDuration.OneMonth) {
            lock.unlockAfter = uint32(block.timestamp) + 30 days;
            lock.receiptAmount = amount / 2;
        } else {
            // LockDuration.None
            lock.unlockAfter = 0;
            lock.receiptAmount = amount / 3;
        }
        return lock;
    }
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
MerkleProof.sol 232 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.20;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The tree and the proofs can be generated using our
 * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
 * You will find a quickstart guide in the readme.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the Merkle tree could be reinterpreted as a leaf value.
 * OpenZeppelin's JavaScript library generates Merkle trees that are safe
 * against this attack out of the box.
 */
library MerkleProof {
    /**
     *@dev The multiproof provided is not valid.
     */
    error MerkleProofInvalidMultiproof();

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Calldata version of {verify}
     */
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofLen = proof.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proofLen != totalHashes + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            if (proofPos != proofLen) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[totalHashes - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Calldata version of {processMultiProof}.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofLen = proof.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proofLen != totalHashes + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            if (proofPos != proofLen) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[totalHashes - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Sorts the pair (a, b) and hashes the result.
     */
    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    /**
     * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
     */
    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

Read Contract

SBIO 0x18b0ea65 → address
VAIX 0xed0bf8e1 → address
VXV 0x77137b3a → address
claimableBalanceOf 0x9c3ee244 → uint256
isMigrationClosed 0x8ee78244 → bool
locks 0xb425f802 → uint256, uint32, uint32, uint32
locksOf 0xc3f9ab58 → tuple[]
maximumMigrationSupply 0x535fd2c5 → uint256
migrationClosesAfter 0x953c1659 → uint32
sbioMigrationCap 0xc168fe0f → uint256
sbioMigrationTotal 0xafb593b5 → uint256
treasury 0x61d027b3 → address
whitelistRoot 0x386bfc98 → bytes32

Write Contract 4 functions

These functions modify contract state and require a wallet transaction to execute.

claim 0x4e71d92d
No parameters
migrateFromSBIO 0xbdac093b
uint256 amount
uint8 lockDuration
bytes32[] whitelistProof
migrateFromVXV 0x0923eb50
uint256 amount
withdrawAfterClosing 0x2dea9ef7
No parameters

Recent Transactions

No transactions found for this address