ETH Price: $2,862.42 (-2.68%)
 

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Withdraw412087842026-01-23 22:21:552 days ago1769206915IN
friend.tech v2.0: Best Friend
0 ETH0.000000210.00300099
Withdraw412087822026-01-23 22:21:512 days ago1769206911IN
friend.tech v2.0: Best Friend
0 ETH0.00000020.00294735
Withdraw412087802026-01-23 22:21:472 days ago1769206907IN
friend.tech v2.0: Best Friend
0 ETH0.000000210.0030087
Withdraw412087192026-01-23 22:19:452 days ago1769206785IN
friend.tech v2.0: Best Friend
0 ETH0.000000230.00324891
Withdraw412087112026-01-23 22:19:292 days ago1769206769IN
friend.tech v2.0: Best Friend
0 ETH0.000000220.00318618
Withdraw412087082026-01-23 22:19:232 days ago1769206763IN
friend.tech v2.0: Best Friend
0 ETH0.000000270.00319895
Withdraw409656722026-01-18 7:18:117 days ago1768720691IN
friend.tech v2.0: Best Friend
0 ETH0.000000250.00306541
Withdraw409166022026-01-17 4:02:319 days ago1768622551IN
friend.tech v2.0: Best Friend
0 ETH0.000000320.00342079
Withdraw409047762026-01-16 21:28:199 days ago1768598899IN
friend.tech v2.0: Best Friend
0 ETH0.000000330.00335526
Withdraw403635662026-01-04 8:47:5921 days ago1767516479IN
friend.tech v2.0: Best Friend
0 ETH0.00000020.00289577
Withdraw403635332026-01-04 8:46:5321 days ago1767516413IN
friend.tech v2.0: Best Friend
0 ETH0.00000020.00291797
Withdraw403635042026-01-04 8:45:5521 days ago1767516355IN
friend.tech v2.0: Best Friend
0 ETH0.00000030.00298298
Withdraw403463572026-01-03 23:14:2122 days ago1767482061IN
friend.tech v2.0: Best Friend
0 ETH0.000000290.00417302
Withdraw403461432026-01-03 23:07:1322 days ago1767481633IN
friend.tech v2.0: Best Friend
0 ETH0.000000440.0052086
Withdraw402282942026-01-01 5:38:5524 days ago1767245935IN
friend.tech v2.0: Best Friend
0 ETH0.000000140.00168657
Withdraw401897342025-12-31 8:13:3525 days ago1767168815IN
friend.tech v2.0: Best Friend
0 ETH0.000000160.00225878
Withdraw401596542025-12-30 15:30:5526 days ago1767108655IN
friend.tech v2.0: Best Friend
0 ETH0.000000280.00296111
Withdraw401557762025-12-30 13:21:3926 days ago1767100899IN
friend.tech v2.0: Best Friend
0 ETH0.000000110.00150573
Withdraw401485252025-12-30 9:19:5726 days ago1767086397IN
friend.tech v2.0: Best Friend
0 ETH0.000000340.00292383
Withdraw400798902025-12-28 19:12:0728 days ago1766949127IN
friend.tech v2.0: Best Friend
0 ETH0.000000080.00098113
Withdraw400383492025-12-27 20:07:2529 days ago1766866045IN
friend.tech v2.0: Best Friend
0 ETH0.000000150.00153007
Withdraw398515222025-12-23 12:19:5133 days ago1766492391IN
friend.tech v2.0: Best Friend
0 ETH0.000000150.0015139
Withdraw397675272025-12-21 13:40:0135 days ago1766324401IN
friend.tech v2.0: Best Friend
0 ETH0.000000290.00378714
Withdraw395724872025-12-17 1:18:4140 days ago1765934321IN
friend.tech v2.0: Best Friend
0 ETH0.000000210.00178614
Withdraw395440002025-12-16 9:29:0740 days ago1765877347IN
friend.tech v2.0: Best Friend
0 ETH0.00000010.00120011
View all transactions

Parent Transaction Hash Block From To
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x2eE4f6c5...47B3234Ab
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
BestFriend

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

/// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import { Owned } from "lib/solmate/src/auth/Owned.sol";
import { IERC20 } from "src/interfaces/IERC20.sol";
import { Points } from "src/Points.sol";
import { IBestFriend } from "src/interfaces/IBestFriend.sol";

// BestFriend is a friend you can trust. He is a friend who gives you points for your
// friendship, and he is a friend who will never let you down.
//
// Note the owner is capable of manipulating of determining distribution of points.
//
// Have fun reading it. Hopefully it's bug-free. God bless.
contract BestFriend is Owned(msg.sender), IBestFriend {
    // Info of each user.
    struct UserInfo {
        uint256 lastDepositBlock; // The last block the user deposited/withdrew LP tokens
        uint256 amount; // How many LP tokens the user has provided.
        uint256 rewardDebt; // Reward debt. See explanation below.
            //
            // We do some fancy math here. Basically, any point in time, the amount of pointss
            // entitled to a user but is pending to be distributed is:
            //
            //   pending reward = (user.amount * pool.accpointsPerShare) - user.rewardDebt
            //
            // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens:
            //   1. The pool's `accpointsPerShare` (and `lastRewardBlock`) gets updated.
            //   2. User receives the pending reward sent to his/her address.
            //   3. User's `amount` gets updated.
            //   4. User's `rewardDebt` gets updated.
    }

    // Info of each pool.
    struct PoolInfo {
        IERC20 lpToken; // Address of LP token contract.
        uint256 lastRewardBlock; // Last block number that pointss distribution occurs.
        uint256 bonusPointsAccrued; // Total number of bonus points that have been received.
        uint256 bonusPointsDistributed; // Bonus points that have been distributed so far.
        uint256 accpointsPerShare; // Accumulated points per share, times wag. See below.
    }

    /*//////////////////////////////////////////////////////////////
                                STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice POINTS!!
    Points public immutable points;
    /// @notice Block number when the reward period ends, unless extended
    uint256 public endBlock;
    /// @notice The number of points distributed per block
    uint256 public immutable pointsPerBlock;
    /// @notice Information stored relating to the BunnySwap Pool
    PoolInfo public pool;
    /// @notice Mapping to keep track of stakers
    mapping(address => UserInfo) public userInfo;
    /// @notice Scale factor for fixed point math, with a meme makerdao name
    uint256 public constant wag = 1e12;
    /// @notice The address of RabbitRouter
    address public immutable router;

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Deposit(address indexed user, uint256 amount);

    event Withdraw(address indexed user, uint256 amount);

    event EmergencyWithdraw(address indexed user, uint256 amount);

    /*
     * @param Points the points contract for onchain points
     * @param _pool Starting data for the pool, note if this is misconfigured the contract *will* malfunction
     * @param _pointsPerBlock The number of points to distribute per block
     * @param _endBlock The block number at which the reward period ends
     * @param _router The address of the router contract
     */
    constructor(Points _points, PoolInfo memory _pool, uint256 _pointsPerBlock, uint256 _endBlock, address _router) {
        points = _points;
        pointsPerBlock = _pointsPerBlock;
        endBlock = _endBlock;

        pool = _pool;
        router = _router;
    }

    /*
     * @param newEndBlock The new end block for the new reward period
     * @safety Points must be sent to BestFriend in order to fund the extended operation
     */
    function extendEndBlock(uint256 newEndBlock) external onlyOwner {
        endBlock = newEndBlock;
    }

    /*//////////////////////////////////////////////////////////////
                                  VIEW
    //////////////////////////////////////////////////////////////*/

    /*
     * @notice View function to see pending points on frontend.
     * @param _user Address of user.
     * 
     * @return Pending points reward for a given user.
     */
    function pendingpoints(address _user) external view returns (uint256) {
        UserInfo storage user = userInfo[_user];
        uint256 accpointsPerShare = pool.accpointsPerShare;
        uint256 lpSupply = pool.lpToken.balanceOf(address(this));
        uint256 lastEligibleBlock = block.number < endBlock ? block.number : endBlock;
        if (block.number > pool.lastRewardBlock && lpSupply != 0) {
            uint256 multiplier;
            if (lastEligibleBlock <= pool.lastRewardBlock) {
                multiplier = 0;
            } else {
                multiplier = lastEligibleBlock - pool.lastRewardBlock;
            }

            uint256 pointsReward = multiplier * pointsPerBlock + pool.bonusPointsAccrued - pool.bonusPointsDistributed;
            accpointsPerShare = accpointsPerShare + pointsReward * wag / lpSupply;
        }
        return user.amount * accpointsPerShare / wag - user.rewardDebt;
    }

    /*
     * @notice Update reward variables of the given pool to be up-to-date.
     */
    function updatePool() public {
        if (block.number <= pool.lastRewardBlock) {
            return;
        }
        uint256 lpSupply = pool.lpToken.balanceOf(address(this));
        if (lpSupply == 0) {
            pool.lastRewardBlock = block.number;
            return;
        }
        uint256 lastEligibleBlock = block.number < endBlock ? block.number : endBlock;

        uint256 multiplier;
        if (lastEligibleBlock <= pool.lastRewardBlock) {
            multiplier = 0;
        } else {
            multiplier = lastEligibleBlock - pool.lastRewardBlock;
        }

        uint256 pointsReward = multiplier * pointsPerBlock + pool.bonusPointsAccrued - pool.bonusPointsDistributed;
        pool.accpointsPerShare = pool.accpointsPerShare + pointsReward * wag / lpSupply;
        pool.lastRewardBlock = block.number;
        pool.bonusPointsDistributed = pool.bonusPointsAccrued;
    }

    /*
     * @notice Let your best friend hold onto your LP tokens for points
     * @param _amount The amount of LP tokens to deposit
     * @param onBehalfOf The address to deposit the LP tokens for, should be msg.sender unless you're the router   
     */
    function deposit(uint256 _amount, address onBehalfOf) external {
        if (onBehalfOf != msg.sender) {
            require(router == msg.sender, "deposit: not authorized");
        }

        UserInfo storage user = userInfo[onBehalfOf];
        updatePool();
        if (user.amount > 0) {
            uint256 pending = ((user.amount * pool.accpointsPerShare) / wag) - user.rewardDebt;
            points.transfer(onBehalfOf, pending);
        }
        pool.lpToken.transferFrom(address(msg.sender), address(this), _amount);
        user.amount = user.amount + _amount;
        user.rewardDebt = user.amount * pool.accpointsPerShare / wag;
        user.lastDepositBlock = block.number;
        emit Deposit(onBehalfOf, _amount);
    }

    /*
     * @notice Withdraw LP tokens from BestFriend
     * @param _amount The amount of LP tokens to withdraw
     * @param onBehalfOf The address to withdraw the LP tokens for, should be msg.sender unless you're the router
     */
    function withdraw(uint256 _amount, address onBehalfOf) external {
        if (onBehalfOf != msg.sender) {
            require(router == msg.sender, "withdraw: not authorized");
        }
        UserInfo storage user = userInfo[onBehalfOf];

        require(user.amount >= _amount, "withdraw: not good");
        require(user.lastDepositBlock + 5 < block.number, "withdraw: wait");

        updatePool();
        uint256 pending = ((user.amount * pool.accpointsPerShare) / wag) - user.rewardDebt;

        points.transfer(onBehalfOf, pending);
        user.amount = user.amount - _amount;
        user.rewardDebt = (user.amount * pool.accpointsPerShare) / wag;
        pool.lpToken.transfer(msg.sender, _amount);
        emit Withdraw(onBehalfOf, _amount);
    }

    /*
     * @notice Lets a contract (Clubs) add to the rewards pool as a bonus
     * @param _amount The amount of points to add to the bonus pool
     */
    function addBonusPoints(uint256 _amount) external {
        pool.bonusPointsAccrued = pool.bonusPointsAccrued + _amount;
        points.transferFrom(msg.sender, address(this), _amount);
    }

    /*
     * @notice Withdraw without collecting rewards, for emergency situations
     * @note I don't think any MasterChef fork has ever needed to use this, but kept it
     *     as it doesn't hurt.
     */
    function emergencyWithdraw() external {
        UserInfo storage user = userInfo[msg.sender];
        pool.lpToken.transfer(address(msg.sender), user.amount);

        emit EmergencyWithdraw(msg.sender, user.amount);

        user.amount = 0;
        user.rewardDebt = 0;
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event OwnershipTransferred(address indexed user, address indexed newOwner);

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}

/// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

interface IERC20 {
    event Approval(address indexed owner, address indexed spender, uint256 value);
    event Transfer(address indexed from, address indexed to, uint256 value);

    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);
    function totalSupply() external view returns (uint256);
    function balanceOf(address owner) external view returns (uint256);
    function allowance(address owner, address spender) external view returns (uint256);

    function approve(address spender, uint256 value) external returns (bool);
    function transfer(address to, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

/// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import { MerkleProofLib } from "lib/solady/src/utils/MerkleProofLib.sol";
import { Owned } from "lib/solmate/src/auth/Owned.sol";
import { IPoints } from "./interfaces/IPoints.sol";

/// @title Points
/// @author CopyPaste
/// @notice A Standard Implementation of Points for FriendTechV2
contract Points is Owned(msg.sender), IPoints {
    /*//////////////////////////////////////////////////////////////
                                 STORAGE
    //////////////////////////////////////////////////////////////*/
    /// Merkle Root for the point claims
    bytes32 public immutable merkleRootA;
    bytes32 public immutable merkleRootB;

    mapping(address user => uint256 balance) public balanceOf;

    mapping(address owner => mapping(address spender => uint256 amount)) public allowance;

    mapping(address user => bool hasClaimedA) public claimedA;
    mapping(address user => bool hasClaimedB) public claimedB;

    /// The totalSupply of points
    uint256 public totalSupply;

    /*//////////////////////////////////////////////////////////////
                                 CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/
    /*
     * @param _merkleRootA The merkle root for first the point claims
     * @param _merkleRootB The merkle root for second the point claims
     */
    constructor(bytes32 _merkleRootA, bytes32 _merkleRootB) {
        merkleRootA = _merkleRootA;
        merkleRootB = _merkleRootB;
    }

    /*//////////////////////////////////////////////////////////////
                                METADATA
    //////////////////////////////////////////////////////////////*/

    string public name = "FRIEND";
    string public symbol = "FRIEND";
    uint8 public decimals = 18;

    /*//////////////////////////////////////////////////////////////
                             ACCESS CONTROL
    //////////////////////////////////////////////////////////////*/
    mapping(address _contract => bool isWhitelisted) public isAllowed;
    mapping(bytes4 functionSignature => bool canBeCalled) public isOpen;

    modifier onlyWhitelisted() {
        require(isAllowed[msg.sender] || isOpen[msg.sig], "WHITELIST");
        _;
    }

    /*
     * @param _contract The contract address allowed to interact with points
     * @param toggle The toggle for whether or not the contract is allowed
     */
    function toggleContract(address _contract, bool toggle) external onlyOwner {
        isAllowed[_contract] = toggle;
    }

    /*
     * @param function The function to open to the public
     * @param toggle The toggle for whether or not the contract is allowed
     */
    function toggleFunction(bytes4 functionSig, bool toggle) external onlyOwner {
        isOpen[functionSig] = toggle;
    }

    /*
     * @param to address to recieve the points
     * @param amount The amount of points to mint
     */
    function mint(address to, uint256 amount) external onlyOwner {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    /*//////////////////////////////////////////////////////////////
                             TRANSFER LOGIC
    //////////////////////////////////////////////////////////////*/
    /*
     * @param proof The merkle proof supplied by the user to prove ownership of the claim
     * @param recipient The address that will receive the claimed points
     * @param amount The amount of points claimed
     */
    function claimA(bytes32[] calldata proof, address recipient, uint256 amount, address[] calldata giftRecipients, uint256 giftAmount) external {
        require(!claimedA[msg.sender], "Points: already claimed");
        require(giftAmount <= amount, "Points: gift amount exceeds claim"); // Would underflow anyway but you can never be too safe
        bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(msg.sender, amount))));
        require(MerkleProofLib.verifyCalldata(proof, merkleRootA, leaf), "Points: invalid proof");

        claimedA[msg.sender] = true;

        if (giftAmount != 0) {
            uint256 perUser = giftAmount / giftRecipients.length;

            amount -= giftAmount;

            for (uint256 i = 0; i < giftRecipients.length; ) {
                balanceOf[giftRecipients[i]] += perUser;

                unchecked {
                    ++i;
                }
            }
        }

        totalSupply += amount + giftAmount;

        // Mint the points to the recipient
        unchecked {
            balanceOf[recipient] += amount;
        }

        emit ClaimedA(msg.sender, recipient, amount);
    }

    /*
     * @param proof The merkle proof supplied by the user to prove ownership of the claim
     * @param recipient The address that will receive the claimed points
     * @param amount The amount of points claimed
     */
    function claimB(bytes32[] calldata proof, address recipient, uint256 amount, address[] calldata giftRecipients, uint256 giftAmount) external {
        require(!claimedB[msg.sender], "Points: already claimed");
        require(giftAmount <= amount, "Points: gift amount exceeds claim"); // Would underflow anyway but you can never be too safe

        bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(msg.sender, amount))));
        require(MerkleProofLib.verifyCalldata(proof, merkleRootB, leaf), "Points: invalid proof");

        claimedB[msg.sender] = true;

        if (giftAmount != 0) {
            uint256 perUser = giftAmount / giftRecipients.length;

            amount -= giftAmount;

            for (uint256 i = 0; i < giftRecipients.length; ) {
                balanceOf[giftRecipients[i]] += perUser;

                unchecked {
                    ++i;
                }
            }
        }

        totalSupply += amount + giftAmount;

        // Mint the points to the recipient
        unchecked {
            balanceOf[recipient] += amount;
        }

        emit ClaimedB(msg.sender, recipient, amount);
    }

    /*
     * @notice Unlike ERC20, Points do not return a boolean on transfer
     * @param to The address that will receive the points
     * @param amount The amount of points to transfer
     */
    function transfer(address to, uint256 amount) external onlyWhitelisted {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);
    }

    /*
     * @notice Unlike ERC20, Points do not return a boolean on transfer
     * @param from The address that will send the points
     * @param to The address that will receive the points
     * @param amount The amount of points to transfer
     */
    function transferFrom(address from, address to, uint256 amount) external onlyWhitelisted {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);
    }

    /*
     * @param spender The address that will be allowed to spend the points
     * @param amount The amount of points the spender will be allowed to spend
     */
    function approve(address spender, uint256 amount) external onlyWhitelisted {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.23;

interface IBestFriend {
    function addBonusPoints(uint256 _amount) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
library MerkleProofLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*            MERKLE PROOF VERIFICATION OPERATIONS            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
        internal
        pure
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(proof) {
                // Initialize `offset` to the offset of `proof` elements in memory.
                let offset := add(proof, 0x20)
                // Left shift by 5 is equivalent to multiplying by 0x20.
                let end := add(offset, shl(5, mload(proof)))
                // Iterate over proof elements to compute root hash.
                for {} 1 {} {
                    // Slot of `leaf` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(leaf, mload(offset)))
                    // Store elements to hash contiguously in scratch space.
                    // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                    mstore(scratch, leaf)
                    mstore(xor(scratch, 0x20), mload(offset))
                    // Reuse `leaf` to store the hash to reduce stack operations.
                    leaf := keccak256(0x00, 0x40)
                    offset := add(offset, 0x20)
                    if iszero(lt(offset, end)) { break }
                }
            }
            isValid := eq(leaf, root)
        }
    }

    /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
        internal
        pure
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if proof.length {
                // Left shift by 5 is equivalent to multiplying by 0x20.
                let end := add(proof.offset, shl(5, proof.length))
                // Initialize `offset` to the offset of `proof` in the calldata.
                let offset := proof.offset
                // Iterate over proof elements to compute root hash.
                for {} 1 {} {
                    // Slot of `leaf` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(leaf, calldataload(offset)))
                    // Store elements to hash contiguously in scratch space.
                    // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                    mstore(scratch, leaf)
                    mstore(xor(scratch, 0x20), calldataload(offset))
                    // Reuse `leaf` to store the hash to reduce stack operations.
                    leaf := keccak256(0x00, 0x40)
                    offset := add(offset, 0x20)
                    if iszero(lt(offset, end)) { break }
                }
            }
            isValid := eq(leaf, root)
        }
    }

    /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
    /// given `proof` and `flags`.
    ///
    /// Note:
    /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
    ///   will always return false.
    /// - The sum of the lengths of `proof` and `leaves` must never overflow.
    /// - Any non-zero word in the `flags` array is treated as true.
    /// - The memory offset of `proof` must be non-zero
    ///   (i.e. `proof` is not pointing to the scratch space).
    function verifyMultiProof(
        bytes32[] memory proof,
        bytes32 root,
        bytes32[] memory leaves,
        bool[] memory flags
    ) internal pure returns (bool isValid) {
        // Rebuilds the root by consuming and producing values on a queue.
        // The queue starts with the `leaves` array, and goes into a `hashes` array.
        // After the process, the last element on the queue is verified
        // to be equal to the `root`.
        //
        // The `flags` array denotes whether the sibling
        // should be popped from the queue (`flag == true`), or
        // should be popped from the `proof` (`flag == false`).
        /// @solidity memory-safe-assembly
        assembly {
            // Cache the lengths of the arrays.
            let leavesLength := mload(leaves)
            let proofLength := mload(proof)
            let flagsLength := mload(flags)

            // Advance the pointers of the arrays to point to the data.
            leaves := add(0x20, leaves)
            proof := add(0x20, proof)
            flags := add(0x20, flags)

            // If the number of flags is correct.
            for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
                // For the case where `proof.length + leaves.length == 1`.
                if iszero(flagsLength) {
                    // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                    isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
                    break
                }

                // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                let proofEnd := add(proof, shl(5, proofLength))
                // We can use the free memory space for the queue.
                // We don't need to allocate, since the queue is temporary.
                let hashesFront := mload(0x40)
                // Copy the leaves into the hashes.
                // Sometimes, a little memory expansion costs less than branching.
                // Should cost less, even with a high free memory offset of 0x7d00.
                leavesLength := shl(5, leavesLength)
                for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
                    mstore(add(hashesFront, i), mload(add(leaves, i)))
                }
                // Compute the back of the hashes.
                let hashesBack := add(hashesFront, leavesLength)
                // This is the end of the memory for the queue.
                // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                flagsLength := add(hashesBack, shl(5, flagsLength))

                for {} 1 {} {
                    // Pop from `hashes`.
                    let a := mload(hashesFront)
                    // Pop from `hashes`.
                    let b := mload(add(hashesFront, 0x20))
                    hashesFront := add(hashesFront, 0x40)

                    // If the flag is false, load the next proof,
                    // else, pops from the queue.
                    if iszero(mload(flags)) {
                        // Loads the next proof.
                        b := mload(proof)
                        proof := add(proof, 0x20)
                        // Unpop from `hashes`.
                        hashesFront := sub(hashesFront, 0x20)
                    }

                    // Advance to the next flag.
                    flags := add(flags, 0x20)

                    // Slot of `a` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(a, b))
                    // Hash the scratch space and push the result onto the queue.
                    mstore(scratch, a)
                    mstore(xor(scratch, 0x20), b)
                    mstore(hashesBack, keccak256(0x00, 0x40))
                    hashesBack := add(hashesBack, 0x20)
                    if iszero(lt(hashesBack, flagsLength)) { break }
                }
                isValid :=
                    and(
                        // Checks if the last value in the queue is same as the root.
                        eq(mload(sub(hashesBack, 0x20)), root),
                        // And whether all the proofs are used, if required.
                        eq(proofEnd, proof)
                    )
                break
            }
        }
    }

    /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
    /// given `proof` and `flags`.
    ///
    /// Note:
    /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
    ///   will always return false.
    /// - Any non-zero word in the `flags` array is treated as true.
    /// - The calldata offset of `proof` must be non-zero
    ///   (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
    function verifyMultiProofCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32[] calldata leaves,
        bool[] calldata flags
    ) internal pure returns (bool isValid) {
        // Rebuilds the root by consuming and producing values on a queue.
        // The queue starts with the `leaves` array, and goes into a `hashes` array.
        // After the process, the last element on the queue is verified
        // to be equal to the `root`.
        //
        // The `flags` array denotes whether the sibling
        // should be popped from the queue (`flag == true`), or
        // should be popped from the `proof` (`flag == false`).
        /// @solidity memory-safe-assembly
        assembly {
            // If the number of flags is correct.
            for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
                // For the case where `proof.length + leaves.length == 1`.
                if iszero(flags.length) {
                    // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                    // forgefmt: disable-next-item
                    isValid := eq(
                        calldataload(
                            xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
                        ),
                        root
                    )
                    break
                }

                // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                let proofEnd := add(proof.offset, shl(5, proof.length))
                // We can use the free memory space for the queue.
                // We don't need to allocate, since the queue is temporary.
                let hashesFront := mload(0x40)
                // Copy the leaves into the hashes.
                // Sometimes, a little memory expansion costs less than branching.
                // Should cost less, even with a high free memory offset of 0x7d00.
                calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
                // Compute the back of the hashes.
                let hashesBack := add(hashesFront, shl(5, leaves.length))
                // This is the end of the memory for the queue.
                // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                flags.length := add(hashesBack, shl(5, flags.length))

                // We don't need to make a copy of `proof.offset` or `flags.offset`,
                // as they are pass-by-value (this trick may not always save gas).

                for {} 1 {} {
                    // Pop from `hashes`.
                    let a := mload(hashesFront)
                    // Pop from `hashes`.
                    let b := mload(add(hashesFront, 0x20))
                    hashesFront := add(hashesFront, 0x40)

                    // If the flag is false, load the next proof,
                    // else, pops from the queue.
                    if iszero(calldataload(flags.offset)) {
                        // Loads the next proof.
                        b := calldataload(proof.offset)
                        proof.offset := add(proof.offset, 0x20)
                        // Unpop from `hashes`.
                        hashesFront := sub(hashesFront, 0x20)
                    }

                    // Advance to the next flag offset.
                    flags.offset := add(flags.offset, 0x20)

                    // Slot of `a` in scratch space.
                    // If the condition is true: 0x20, otherwise: 0x00.
                    let scratch := shl(5, gt(a, b))
                    // Hash the scratch space and push the result onto the queue.
                    mstore(scratch, a)
                    mstore(xor(scratch, 0x20), b)
                    mstore(hashesBack, keccak256(0x00, 0x40))
                    hashesBack := add(hashesBack, 0x20)
                    if iszero(lt(hashesBack, flags.length)) { break }
                }
                isValid :=
                    and(
                        // Checks if the last value in the queue is same as the root.
                        eq(mload(sub(hashesBack, 0x20)), root),
                        // And whether all the proofs are used, if required.
                        eq(proofEnd, proof.offset)
                    )
                break
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes32 array.
    function emptyProof() internal pure returns (bytes32[] calldata proof) {
        /// @solidity memory-safe-assembly
        assembly {
            proof.length := 0
        }
    }

    /// @dev Returns an empty calldata bytes32 array.
    function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
        /// @solidity memory-safe-assembly
        assembly {
            leaves.length := 0
        }
    }

    /// @dev Returns an empty calldata bool array.
    function emptyFlags() internal pure returns (bool[] calldata flags) {
        /// @solidity memory-safe-assembly
        assembly {
            flags.length := 0
        }
    }
}

/// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

interface IPoints {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice By maintaining ERC20 compatibility, transfers and approvals are easier to index
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    /// @notice Keep track of whether or not a user has claimed their points
    event ClaimedA(address indexed _owner, address indexed recipient, uint256 indexed amount);
    event ClaimedB(address indexed _owner, address indexed recipient, uint256 indexed amount);
    /*//////////////////////////////////////////////////////////////
                               FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /*
     * @param proof The merkle proof supplied by the user to prove ownership of the claim
     * @param recipient The address that will receive the claimed points
     * @param amount The amount of points claimed
     */
    function claimA(bytes32[] calldata proof, address recipient, uint256 amount, address[] calldata giftRecipients, uint256 giftAmount) external;

    function claimB(bytes32[] calldata proof, address recipient, uint256 amount, address[] calldata giftRecipients, uint256 giftAmount) external;

    /*
     * @notice Unlike ERC20, Points do not return a boolean on transfer
     * @param to The address that will receive the points
     * @param value The amount of points to transfer
     */
    function transfer(address to, uint256 value) external;

    /*
     * @notice Unlike ERC20, Points do not return a boolean on transfer
     * @param from The address that will send the points
     * @param to The address that will receive the points
     * @param value The amount of points to transfer
     */
    function transferFrom(address from, address to, uint256 value) external;

    /*
     * @param spender The address that will be allowed to spend the points
     * @param amount The amount of points the spender will be allowed to spend
     */
    function approve(address spender, uint256 amount) external;

    /*//////////////////////////////////////////////////////////////
                             VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /*
     * @param _owner The address that will be allowed to spend the points
     * @param _spender The address that will be allowed to spend the points
     * 
     * @return The amount of points the spender is allowed to spend
     */
    function allowance(address _owner, address _spender) external view returns (uint256);

    /*
     * @param _owner The address that will be checked for points
     * 
     * @return The amount of points the owner has
     */
    function balanceOf(address _owner) external view returns (uint256);

    /*
     * @return The root of the Merkle Tree which stores claims
     */
    function merkleRootA() external view returns (bytes32);

    /*//////////////////////////////////////////////////////////////
                             ACCESS CONTROL
    //////////////////////////////////////////////////////////////*/
    /*
     * @param _address The address in question
     * 
     * @return Whether the contract is allowed to interact with points 
     */
    function isAllowed(address _address) external view returns (bool);
}

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "murky/=lib/murky/",
    "openzeppelin-contracts/=lib/murky/lib/openzeppelin-contracts/",
    "solady/=lib/solady/src/",
    "solmate/=lib/solmate/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract Points","name":"_points","type":"address"},{"components":[{"internalType":"contract IERC20","name":"lpToken","type":"address"},{"internalType":"uint256","name":"lastRewardBlock","type":"uint256"},{"internalType":"uint256","name":"bonusPointsAccrued","type":"uint256"},{"internalType":"uint256","name":"bonusPointsDistributed","type":"uint256"},{"internalType":"uint256","name":"accpointsPerShare","type":"uint256"}],"internalType":"struct BestFriend.PoolInfo","name":"_pool","type":"tuple"},{"internalType":"uint256","name":"_pointsPerBlock","type":"uint256"},{"internalType":"uint256","name":"_endBlock","type":"uint256"},{"internalType":"address","name":"_router","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"addBonusPoints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newEndBlock","type":"uint256"}],"name":"extendEndBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"pendingpoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"points","outputs":[{"internalType":"contract Points","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pointsPerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pool","outputs":[{"internalType":"contract IERC20","name":"lpToken","type":"address"},{"internalType":"uint256","name":"lastRewardBlock","type":"uint256"},{"internalType":"uint256","name":"bonusPointsAccrued","type":"uint256"},{"internalType":"uint256","name":"bonusPointsDistributed","type":"uint256"},{"internalType":"uint256","name":"accpointsPerShare","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updatePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"lastDepositBlock","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rewardDebt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wag","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

0x60e06040523480156200001157600080fd5b5060405162001410380380620014108339810160408190526200003491620000fd565b600080546001600160a01b031916339081178255604051909182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506001600160a01b03948516608090815260a0939093526001919091558251600280546001600160a01b0319169186169190911790556020830151600355604083015160045560608301516005559101516006551660c052620001d4565b6001600160a01b0381168114620000e857600080fd5b50565b8051620000f881620000d2565b919050565b60008060008060008587036101208112156200011857600080fd5b86516200012581620000d2565b955060a0601f19820112156200013a57600080fd5b5060405160a081016001600160401b03811182821017156200016c57634e487b7160e01b600052604160045260246000fd5b6040526200017d60208801620000eb565b815260408701516020820152606087015160408201526080870151606082015260a087015160808201528094505060c0860151925060e08601519150620001c86101008701620000eb565b90509295509295909350565b60805160a05160c0516111db620002356000396000818161030101528181610342015261086f01526000818161023f01528181610c650152610efd0152600081816101f30152818161057a0152818161075601526109c801526111db6000f3fe608060405234801561001057600080fd5b50600436106100ff5760003560e01c80636e553f6511610097578063e3161ddd11610066578063e3161ddd146102d5578063f2fde38b146102dd578063f5dbced2146102f0578063f887ea40146102fc57600080fd5b80636e553f65146102875780638da5cb5b1461029a578063afa0120b146102ba578063db2e21bc146102cd57600080fd5b80631be6dd64116100d35780631be6dd64146101ee5780633fdb1e8f1461023a57806353e82334146102615780636dcec4b81461027457600080fd5b8062f714ce14610104578063083c63231461011957806316f0115b146101355780631959a002146101a4575b600080fd5b61011761011236600461108c565b610323565b005b61012260015481565b6040519081526020015b60405180910390f35b6002546003546004546005546006546101659473ffffffffffffffffffffffffffffffffffffffff169392919085565b6040805173ffffffffffffffffffffffffffffffffffffffff90961686526020860194909452928401919091526060830152608082015260a00161012c565b6101d36101b23660046110b8565b60076020526000908152604090208054600182015460029092015490919083565b6040805193845260208401929092529082015260600161012c565b6102157f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161012c565b6101227f000000000000000000000000000000000000000000000000000000000000000081565b61011761026f3660046110da565b610706565b6101176102823660046110da565b6107ca565b61011761029536600461108c565b610850565b6000546102159073ffffffffffffffffffffffffffffffffffffffff1681565b6101226102c83660046110b8565b610b57565b610117610d02565b610117610e02565b6101176102eb3660046110b8565b610f72565b61012264e8d4a5100081565b6102157f000000000000000000000000000000000000000000000000000000000000000081565b73ffffffffffffffffffffffffffffffffffffffff811633146103e4577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1633146103e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f77697468647261773a206e6f7420617574686f72697a6564000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811660009081526007602052604090206001810154831115610477576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f77697468647261773a206e6f7420676f6f64000000000000000000000000000060448201526064016103db565b80544390610486906005611122565b106104ed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f77697468647261773a207761697400000000000000000000000000000000000060448201526064016103db565b6104f5610e02565b6000816002015464e8d4a510006002600401548460010154610517919061113b565b6105219190611152565b61052b919061118d565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152602482018390529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb90604401600060405180830381600087803b1580156105c057600080fd5b505af11580156105d4573d6000803e3d6000fd5b505050508382600101546105e8919061118d565b6001830181905560065464e8d4a5100091610603919061113b565b61060d9190611152565b600283810191909155546040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526024810186905273ffffffffffffffffffffffffffffffffffffffff9091169063a9059cbb906044016020604051808303816000875af115801561068b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106af91906111a0565b508273ffffffffffffffffffffffffffffffffffffffff167f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364856040516106f891815260200190565b60405180910390a250505050565b600454610714908290611122565b60049081556040517f23b872dd0000000000000000000000000000000000000000000000000000000081523391810191909152306024820152604481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906323b872dd90606401600060405180830381600087803b1580156107af57600080fd5b505af11580156107c3573d6000803e3d6000fd5b5050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461084b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016103db565b600155565b73ffffffffffffffffffffffffffffffffffffffff8116331461090c577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16331461090c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f6465706f7369743a206e6f7420617574686f72697a656400000000000000000060448201526064016103db565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600760205260409020610939610e02565b600181015415610a28576000816002015464e8d4a510006002600401548460010154610965919061113b565b61096f9190611152565b610979919061118d565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152602482018390529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb90604401600060405180830381600087803b158015610a0e57600080fd5b505af1158015610a22573d6000803e3d6000fd5b50505050505b6002546040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810185905273ffffffffffffffffffffffffffffffffffffffff909116906323b872dd906064016020604051808303816000875af1158015610aa5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ac991906111a0565b50828160010154610ada9190611122565b6001820181905560065464e8d4a5100091610af5919061113b565b610aff9190611152565b600282015543815560405183815273ffffffffffffffffffffffffffffffffffffffff8316907fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9060200160405180910390a2505050565b73ffffffffffffffffffffffffffffffffffffffff81811660009081526007602052604080822060065460025492517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152939491939092859216906370a0823190602401602060405180830381865afa158015610bdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c0391906111c2565b905060006001544310610c1857600154610c1a565b435b60035490915043118015610c2d57508115155b15610cc9576003546000908211610c4657506000610c56565b600354610c53908361118d565b90505b60055460045460009190610c8a7f00000000000000000000000000000000000000000000000000000000000000008561113b565b610c949190611122565b610c9e919061118d565b905083610cb064e8d4a510008361113b565b610cba9190611152565b610cc49086611122565b945050505b836002015464e8d4a51000848660010154610ce4919061113b565b610cee9190611152565b610cf8919061118d565b9695505050505050565b3360008181526007602052604090819020600254600182015492517fa9059cbb000000000000000000000000000000000000000000000000000000008152600481019490945260248401929092529173ffffffffffffffffffffffffffffffffffffffff9091169063a9059cbb906044016020604051808303816000875af1158015610d92573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610db691906111a0565b50600181015460405190815233907f5fafa99d0643513820be26656b45130b01e1c03062e1266bf36f88cbd3bd96959060200160405180910390a2600060018201819055600290910155565b6003544311610e0d57565b6002546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610e7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea091906111c2565b905080600003610eb1575043600355565b60006001544310610ec457600154610ec6565b435b905060006002600101548211610ede57506000610eee565b600354610eeb908361118d565b90505b60055460045460009190610f227f00000000000000000000000000000000000000000000000000000000000000008561113b565b610f2c9190611122565b610f36919061118d565b905083610f4864e8d4a510008361113b565b610f529190611152565b600654610f5f9190611122565b6006555050436003555050600454600555565b60005473ffffffffffffffffffffffffffffffffffffffff163314610ff3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016103db565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b803573ffffffffffffffffffffffffffffffffffffffff8116811461108757600080fd5b919050565b6000806040838503121561109f57600080fd5b823591506110af60208401611063565b90509250929050565b6000602082840312156110ca57600080fd5b6110d382611063565b9392505050565b6000602082840312156110ec57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115611135576111356110f3565b92915050565b8082028115828204841417611135576111356110f3565b600082611188577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b81810381811115611135576111356110f3565b6000602082840312156111b257600080fd5b815180151581146110d357600080fd5b6000602082840312156111d457600080fd5b5051919050560000000000000000000000000bd4887f7d41b35cd75dff9ffee2856106f866700000000000000000000000007cfc830448484cdf830625373820241e61ef4acf0000000000000000000000000000000000000000000000000000000000d4b3c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a8fb8c0bd6a60000000000000000000000000000000000000000000000000000000000001c54d87000000000000000000000000bf250ae227de43dedaf01ccbfd8cc83027efc1e2

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100ff5760003560e01c80636e553f6511610097578063e3161ddd11610066578063e3161ddd146102d5578063f2fde38b146102dd578063f5dbced2146102f0578063f887ea40146102fc57600080fd5b80636e553f65146102875780638da5cb5b1461029a578063afa0120b146102ba578063db2e21bc146102cd57600080fd5b80631be6dd64116100d35780631be6dd64146101ee5780633fdb1e8f1461023a57806353e82334146102615780636dcec4b81461027457600080fd5b8062f714ce14610104578063083c63231461011957806316f0115b146101355780631959a002146101a4575b600080fd5b61011761011236600461108c565b610323565b005b61012260015481565b6040519081526020015b60405180910390f35b6002546003546004546005546006546101659473ffffffffffffffffffffffffffffffffffffffff169392919085565b6040805173ffffffffffffffffffffffffffffffffffffffff90961686526020860194909452928401919091526060830152608082015260a00161012c565b6101d36101b23660046110b8565b60076020526000908152604090208054600182015460029092015490919083565b6040805193845260208401929092529082015260600161012c565b6102157f0000000000000000000000000bd4887f7d41b35cd75dff9ffee2856106f8667081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161012c565b6101227f0000000000000000000000000000000000000000000000000a8fb8c0bd6a600081565b61011761026f3660046110da565b610706565b6101176102823660046110da565b6107ca565b61011761029536600461108c565b610850565b6000546102159073ffffffffffffffffffffffffffffffffffffffff1681565b6101226102c83660046110b8565b610b57565b610117610d02565b610117610e02565b6101176102eb3660046110b8565b610f72565b61012264e8d4a5100081565b6102157f000000000000000000000000bf250ae227de43dedaf01ccbfd8cc83027efc1e281565b73ffffffffffffffffffffffffffffffffffffffff811633146103e4577f000000000000000000000000bf250ae227de43dedaf01ccbfd8cc83027efc1e273ffffffffffffffffffffffffffffffffffffffff1633146103e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f77697468647261773a206e6f7420617574686f72697a6564000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811660009081526007602052604090206001810154831115610477576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f77697468647261773a206e6f7420676f6f64000000000000000000000000000060448201526064016103db565b80544390610486906005611122565b106104ed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f77697468647261773a207761697400000000000000000000000000000000000060448201526064016103db565b6104f5610e02565b6000816002015464e8d4a510006002600401548460010154610517919061113b565b6105219190611152565b61052b919061118d565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152602482018390529192507f0000000000000000000000000bd4887f7d41b35cd75dff9ffee2856106f866709091169063a9059cbb90604401600060405180830381600087803b1580156105c057600080fd5b505af11580156105d4573d6000803e3d6000fd5b505050508382600101546105e8919061118d565b6001830181905560065464e8d4a5100091610603919061113b565b61060d9190611152565b600283810191909155546040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526024810186905273ffffffffffffffffffffffffffffffffffffffff9091169063a9059cbb906044016020604051808303816000875af115801561068b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106af91906111a0565b508273ffffffffffffffffffffffffffffffffffffffff167f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364856040516106f891815260200190565b60405180910390a250505050565b600454610714908290611122565b60049081556040517f23b872dd0000000000000000000000000000000000000000000000000000000081523391810191909152306024820152604481018290527f0000000000000000000000000bd4887f7d41b35cd75dff9ffee2856106f8667073ffffffffffffffffffffffffffffffffffffffff16906323b872dd90606401600060405180830381600087803b1580156107af57600080fd5b505af11580156107c3573d6000803e3d6000fd5b5050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461084b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016103db565b600155565b73ffffffffffffffffffffffffffffffffffffffff8116331461090c577f000000000000000000000000bf250ae227de43dedaf01ccbfd8cc83027efc1e273ffffffffffffffffffffffffffffffffffffffff16331461090c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f6465706f7369743a206e6f7420617574686f72697a656400000000000000000060448201526064016103db565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600760205260409020610939610e02565b600181015415610a28576000816002015464e8d4a510006002600401548460010154610965919061113b565b61096f9190611152565b610979919061118d565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152602482018390529192507f0000000000000000000000000bd4887f7d41b35cd75dff9ffee2856106f866709091169063a9059cbb90604401600060405180830381600087803b158015610a0e57600080fd5b505af1158015610a22573d6000803e3d6000fd5b50505050505b6002546040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810185905273ffffffffffffffffffffffffffffffffffffffff909116906323b872dd906064016020604051808303816000875af1158015610aa5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ac991906111a0565b50828160010154610ada9190611122565b6001820181905560065464e8d4a5100091610af5919061113b565b610aff9190611152565b600282015543815560405183815273ffffffffffffffffffffffffffffffffffffffff8316907fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9060200160405180910390a2505050565b73ffffffffffffffffffffffffffffffffffffffff81811660009081526007602052604080822060065460025492517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152939491939092859216906370a0823190602401602060405180830381865afa158015610bdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c0391906111c2565b905060006001544310610c1857600154610c1a565b435b60035490915043118015610c2d57508115155b15610cc9576003546000908211610c4657506000610c56565b600354610c53908361118d565b90505b60055460045460009190610c8a7f0000000000000000000000000000000000000000000000000a8fb8c0bd6a60008561113b565b610c949190611122565b610c9e919061118d565b905083610cb064e8d4a510008361113b565b610cba9190611152565b610cc49086611122565b945050505b836002015464e8d4a51000848660010154610ce4919061113b565b610cee9190611152565b610cf8919061118d565b9695505050505050565b3360008181526007602052604090819020600254600182015492517fa9059cbb000000000000000000000000000000000000000000000000000000008152600481019490945260248401929092529173ffffffffffffffffffffffffffffffffffffffff9091169063a9059cbb906044016020604051808303816000875af1158015610d92573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610db691906111a0565b50600181015460405190815233907f5fafa99d0643513820be26656b45130b01e1c03062e1266bf36f88cbd3bd96959060200160405180910390a2600060018201819055600290910155565b6003544311610e0d57565b6002546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610e7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea091906111c2565b905080600003610eb1575043600355565b60006001544310610ec457600154610ec6565b435b905060006002600101548211610ede57506000610eee565b600354610eeb908361118d565b90505b60055460045460009190610f227f0000000000000000000000000000000000000000000000000a8fb8c0bd6a60008561113b565b610f2c9190611122565b610f36919061118d565b905083610f4864e8d4a510008361113b565b610f529190611152565b600654610f5f9190611122565b6006555050436003555050600454600555565b60005473ffffffffffffffffffffffffffffffffffffffff163314610ff3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016103db565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b803573ffffffffffffffffffffffffffffffffffffffff8116811461108757600080fd5b919050565b6000806040838503121561109f57600080fd5b823591506110af60208401611063565b90509250929050565b6000602082840312156110ca57600080fd5b6110d382611063565b9392505050565b6000602082840312156110ec57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115611135576111356110f3565b92915050565b8082028115828204841417611135576111356110f3565b600082611188577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b81810381811115611135576111356110f3565b6000602082840312156111b257600080fd5b815180151581146110d357600080fd5b6000602082840312156111d457600080fd5b505191905056

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.