Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x392440db3BeF15ea5E9aAdE9ddf72e923556001E
Balance 0 ETH
Nonce 1
Code Size 8238 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

8238 bytes
0x6080604052600436106100fd575f3560e01c80638da5cb5b11610094578063f04e283e11610063578063f04e283e146102bf578063f2fde38b146102db578063f83d08ba146102f7578063fee81cf414610321578063ff9413d81461035d576100fd565b80638da5cb5b146101ff5780639394d2e814610229578063b0f479a114610259578063dfcf048d14610283576100fd565b80633fc8cef3116100d05780633fc8cef31461019957806354d1f13d146101c3578063715018a6146101cd57806385572ffb146101d7576100fd565b806301ffc9a71461010157806302a0ba3f1461013d578063236e7cb414610165578063256929621461018f575b5f5ffd5b34801561010c575f5ffd5b50610127600480360381019061012291906114a5565b610373565b60405161013491906114ea565b60405180910390f35b348015610148575f5ffd5b50610163600480360381019061015e9190611590565b610444565b005b348015610170575f5ffd5b506101796105bd565b6040516101869190611629565b60405180910390f35b6101976105e1565b005b3480156101a4575f5ffd5b506101ad610632565b6040516101ba9190611662565b60405180910390f35b6101cb610656565b005b6101d561068f565b005b3480156101e2575f5ffd5b506101fd60048036038101906101f8919061169d565b6106a2565b005b34801561020a575f5ffd5b5061021361072e565b60405161022091906116f3565b60405180910390f35b610243600480360381019061023e9190611590565b610756565b6040516102509190611724565b60405180910390f35b348015610264575f5ffd5b5061026d610ca3565b60405161027a91906116f3565b60405180910390f35b34801561028e575f5ffd5b506102a960048036038101906102a49190611590565b610cca565b6040516102b6919061174c565b60405180910390f35b6102d960048036038101906102d49190611765565b610e9a565b005b6102f560048036038101906102f09190611765565b610ed8565b005b348015610302575f5ffd5b5061030b610f01565b60405161031891906114ea565b60405180910390f35b34801561032c575f5ffd5b5061034760048036038101906103429190611765565b610f13565b604051610354919061174c565b60405180910390f35b348015610368575f5ffd5b50610371610f2c565b005b5f7f85572ffb000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061043d57507f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b9050919050565b61044c610f5e565b817f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016104a691906116f3565b602060405180830381865afa1580156104c1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104e591906117a4565b101561051d576040517f6f2750f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663a9059cbb82846040518363ffffffff1660e01b81526004016105789291906117cf565b6020604051808303815f875af1158015610594573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105b89190611820565b505050565b7f000000000000000000000000570f09ac53b96929e3868f71864e36ff6b1b67d781565b5f6105ea610f95565b67ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f5fa250565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f5fa2565b610697610f5e565b6106a05f610f9f565b565b6106aa610ca3565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461071957336040517fd7f7333400000000000000000000000000000000000000000000000000000000815260040161071091906116f3565b60405180910390fd5b61072b8161072690611be4565b611065565b50565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754905090565b5f61075f611338565b60015f9054906101000a900460ff16156107a5576040517f83e3abf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361080a576040517f9fd720a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8303610843576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6040518060a001604052803060405160200161086091906116f3565b60405160208183030381529060405281526020018585604051602001610887929190611bf6565b60405160208183030381529060405281526020015f67ffffffffffffffff8111156108b5576108b461185f565b5b6040519080825280602002602001820160405280156108ee57816020015b6108db611411565b8152602001906001900390816108d35790505b5081526020017f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16815260200161094a60405180602001604052806201fbd0815250611385565b81525090507f000000000000000000000000570f09ac53b96929e3868f71864e36ff6b1b67d773ffffffffffffffffffffffffffffffffffffffff166323b872dd3330876040518463ffffffff1660e01b81526004016109ac93929190611c1d565b6020604051808303815f875af11580156109c8573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109ec9190611820565b505f7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610a4791906116f3565b602060405180830381865afa158015610a62573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a8691906117a4565b90507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff1660e01b81526004015f604051808303818588803b158015610aee575f5ffd5b505af1158015610b00573d5f5f3e3d5ffd5b50505050507f00000000000000000000000080226fc0ee2b096224eeac085bb9a8cba1146f7d73ffffffffffffffffffffffffffffffffffffffff166396f4e9f97f000000000000000000000000000000000000000000000000ad8bef0a6a427394846040518363ffffffff1660e01b8152600401610b80929190611e3c565b6020604051808303815f875af1158015610b9c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bc09190611e7e565b92507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610c1b91906116f3565b602060405180830381865afa158015610c36573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c5a91906117a4565b811115610c93576040517f903fbf4600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050610c9d611404565b92915050565b5f7f00000000000000000000000080226fc0ee2b096224eeac085bb9a8cba1146f7d905090565b5f5f6040518060a0016040528030604051602001610ce891906116f3565b60405160208183030381529060405281526020018585604051602001610d0f929190611bf6565b60405160208183030381529060405281526020015f67ffffffffffffffff811115610d3d57610d3c61185f565b5b604051908082528060200260200182016040528015610d7657816020015b610d63611411565b815260200190600190039081610d5b5790505b5081526020017f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff168152602001610dd260405180602001604052806201fbd0815250611385565b81525090507f00000000000000000000000080226fc0ee2b096224eeac085bb9a8cba1146f7d73ffffffffffffffffffffffffffffffffffffffff166320487ded7f000000000000000000000000000000000000000000000000ad8bef0a6a427394836040518363ffffffff1660e01b8152600401610e52929190611e3c565b602060405180830381865afa158015610e6d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e9191906117a4565b91505092915050565b610ea2610f5e565b63389a75e1600c52805f526020600c208054421115610ec857636f5e88185f526004601cfd5b5f815550610ed581610f9f565b50565b610ee0610f5e565b8060601b610ef557637448fbae5f526004601cfd5b610efe81610f9f565b50565b60015f9054906101000a900460ff1681565b5f63389a75e1600c52815f526020600c20549050919050565b610f34610f5e565b60015f9054906101000a900460ff161560015f6101000a81548160ff021916908315150217905550565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543314610f93576382b429005f526004601cfd5b565b5f6202a300905090565b610fa761140d565b1561100c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f5fa3811560ff1b8217815550611062565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278160601b60601c91508181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f5fa3818155505b50565b61106d611338565b7f000000000000000000000000000000000000000000000000ad8bef0a6a42739467ffffffffffffffff16816020015167ffffffffffffffff16146110de576040517f656535ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff16816040015180602001905181019061110d9190611ee4565b73ffffffffffffffffffffffffffffffffffffffff161461115a576040517fddb5de5e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015f9054906101000a900460ff16156111a0576040517f83e3abf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5f82606001518060200190518101906111ba9190611f0f565b91509150817f000000000000000000000000570f09ac53b96929e3868f71864e36ff6b1b67d773ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161121891906116f3565b602060405180830381865afa158015611233573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061125791906117a4565b101561128f576040517fe4455cae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000570f09ac53b96929e3868f71864e36ff6b1b67d773ffffffffffffffffffffffffffffffffffffffff1663a9059cbb82846040518363ffffffff1660e01b81526004016112ea9291906117cf565b6020604051808303815f875af1158015611306573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061132a9190611820565b505050611335611404565b50565b60025f540361137c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161137390611fa7565b60405180910390fd5b60025f81905550565b60606397a657c960e01b826040516024016113a09190611fdf565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050919050565b60015f81905550565b5f90565b60405180604001604052805f73ffffffffffffffffffffffffffffffffffffffff1681526020015f81525090565b5f604051905090565b5f5ffd5b5f5ffd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61148481611450565b811461148e575f5ffd5b50565b5f8135905061149f8161147b565b92915050565b5f602082840312156114ba576114b9611448565b5b5f6114c784828501611491565b91505092915050565b5f8115159050919050565b6114e4816114d0565b82525050565b5f6020820190506114fd5f8301846114db565b92915050565b5f819050919050565b61151581611503565b811461151f575f5ffd5b50565b5f813590506115308161150c565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61155f82611536565b9050919050565b61156f81611555565b8114611579575f5ffd5b50565b5f8135905061158a81611566565b92915050565b5f5f604083850312156115a6576115a5611448565b5b5f6115b385828601611522565b92505060206115c48582860161157c565b9150509250929050565b5f819050919050565b5f6115f16115ec6115e784611536565b6115ce565b611536565b9050919050565b5f611602826115d7565b9050919050565b5f611613826115f8565b9050919050565b61162381611609565b82525050565b5f60208201905061163c5f83018461161a565b92915050565b5f61164c826115f8565b9050919050565b61165c81611642565b82525050565b5f6020820190506116755f830184611653565b92915050565b5f5ffd5b5f60a082840312156116945761169361167b565b5b81905092915050565b5f602082840312156116b2576116b1611448565b5b5f82013567ffffffffffffffff8111156116cf576116ce61144c565b5b6116db8482850161167f565b91505092915050565b6116ed81611555565b82525050565b5f6020820190506117065f8301846116e4565b92915050565b5f819050919050565b61171e8161170c565b82525050565b5f6020820190506117375f830184611715565b92915050565b61174681611503565b82525050565b5f60208201905061175f5f83018461173d565b92915050565b5f6020828403121561177a57611779611448565b5b5f6117878482850161157c565b91505092915050565b5f8151905061179e8161150c565b92915050565b5f602082840312156117b9576117b8611448565b5b5f6117c684828501611790565b91505092915050565b5f6040820190506117e25f8301856116e4565b6117ef602083018461173d565b9392505050565b6117ff816114d0565b8114611809575f5ffd5b50565b5f8151905061181a816117f6565b92915050565b5f6020828403121561183557611834611448565b5b5f6118428482850161180c565b91505092915050565b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6118958261184f565b810181811067ffffffffffffffff821117156118b4576118b361185f565b5b80604052505050565b5f6118c661143f565b90506118d2828261188c565b919050565b5f5ffd5b6118e48161170c565b81146118ee575f5ffd5b50565b5f813590506118ff816118db565b92915050565b5f67ffffffffffffffff82169050919050565b61192181611905565b811461192b575f5ffd5b50565b5f8135905061193c81611918565b92915050565b5f5ffd5b5f5ffd5b5f67ffffffffffffffff8211156119645761196361185f565b5b61196d8261184f565b9050602081019050919050565b828183375f83830152505050565b5f61199a6119958461194a565b6118bd565b9050828152602081018484840111156119b6576119b5611946565b5b6119c184828561197a565b509392505050565b5f82601f8301126119dd576119dc611942565b5b81356119ed848260208601611988565b91505092915050565b5f67ffffffffffffffff821115611a1057611a0f61185f565b5b602082029050602081019050919050565b5f5ffd5b5f60408284031215611a3a57611a3961184b565b5b611a4460406118bd565b90505f611a538482850161157c565b5f830152506020611a6684828501611522565b60208301525092915050565b5f611a84611a7f846119f6565b6118bd565b90508083825260208201905060408402830185811115611aa757611aa6611a21565b5b835b81811015611ad05780611abc8882611a25565b845260208401935050604081019050611aa9565b5050509392505050565b5f82601f830112611aee57611aed611942565b5b8135611afe848260208601611a72565b91505092915050565b5f60a08284031215611b1c57611b1b61184b565b5b611b2660a06118bd565b90505f611b35848285016118f1565b5f830152506020611b488482850161192e565b602083015250604082013567ffffffffffffffff811115611b6c57611b6b6118d7565b5b611b78848285016119c9565b604083015250606082013567ffffffffffffffff811115611b9c57611b9b6118d7565b5b611ba8848285016119c9565b606083015250608082013567ffffffffffffffff811115611bcc57611bcb6118d7565b5b611bd884828501611ada565b60808301525092915050565b5f611bef3683611b07565b9050919050565b5f604082019050611c095f83018561173d565b611c1660208301846116e4565b9392505050565b5f606082019050611c305f8301866116e4565b611c3d60208301856116e4565b611c4a604083018461173d565b949350505050565b611c5b81611905565b82525050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f611c9382611c61565b611c9d8185611c6b565b9350611cad818560208601611c7b565b611cb68161184f565b840191505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b611cf381611555565b82525050565b611d0281611503565b82525050565b604082015f820151611d1c5f850182611cea565b506020820151611d2f6020850182611cf9565b50505050565b5f611d408383611d08565b60408301905092915050565b5f602082019050919050565b5f611d6282611cc1565b611d6c8185611ccb565b9350611d7783611cdb565b805f5b83811015611da7578151611d8e8882611d35565b9750611d9983611d4c565b925050600181019050611d7a565b5085935050505092915050565b5f60a083015f8301518482035f860152611dce8282611c89565b91505060208301518482036020860152611de88282611c89565b91505060408301518482036040860152611e028282611d58565b9150506060830151611e176060860182611cea565b5060808301518482036080860152611e2f8282611c89565b9150508091505092915050565b5f604082019050611e4f5f830185611c52565b8181036020830152611e618184611db4565b90509392505050565b5f81519050611e78816118db565b92915050565b5f60208284031215611e9357611e92611448565b5b5f611ea084828501611e6a565b91505092915050565b5f611eb382611536565b9050919050565b611ec381611ea9565b8114611ecd575f5ffd5b50565b5f81519050611ede81611eba565b92915050565b5f60208284031215611ef957611ef8611448565b5b5f611f0684828501611ed0565b91505092915050565b5f5f60408385031215611f2557611f24611448565b5b5f611f3285828601611790565b9250506020611f4385828601611ed0565b9150509250929050565b5f82825260208201905092915050565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c005f82015250565b5f611f91601f83611f4d565b9150611f9c82611f5d565b602082019050919050565b5f6020820190508181035f830152611fbe81611f85565b9050919050565b602082015f820151611fd95f850182611cf9565b50505050565b5f602082019050611ff25f830184611fc5565b9291505056fea264697066735822122001295a826adf4479fcbc28ef6687c873a0370dab38afefe4b53780ef92e0db9564736f6c634300081c0033

Verified Source Code Full Match

Compiler: v0.8.28+commit.7893614a EVM: cancun Optimization: No
Bridge.sol 251 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import "https://github.com/Vectorized/solady/blob/main/src/auth/Ownable.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

interface IWETH {
    function deposit() external payable;
    function withdraw(uint256 amount) external;
    function transfer(address to, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
    function approve(address spender, uint256 value) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function totalSupply() external view returns (uint256);
}

contract CCIPBridgeConfiguration is Ownable {
    address router;
    address weth;
    uint64 destinationChainSelector;
    address bridgeToken;

    /// @param _owner The owner of the configuration contract, will be passed to the Bridge contract.
    constructor(address _owner) Ownable() {
        _initializeOwner(_owner);
    }

    /// @param _router The address of the router contract.
    /// @param _weth The address of the weth contract.
    /// @param _destinationChainSelector The address of the destination chain selector, this is immutable.
    /// @param _bridgeToken The address of the bridge token, this is immutable.
    function setConfiguration(
        address _router, 
        address _weth, 
        uint64 _destinationChainSelector,
        address _bridgeToken
    ) external onlyOwner {
        router = _router;
        weth = _weth;
        destinationChainSelector = _destinationChainSelector;
        bridgeToken = _bridgeToken;
    }

    function getRouter() external view returns(address _router) {
        _router = router;
    }

    function getConfiguration() external view returns(
        address _router, 
        address _weth, 
        uint64 _destinationChainSelector,
        address _bridgeToken
    ) {
        _router = router;
        _weth = weth;
        _destinationChainSelector = destinationChainSelector;
        _bridgeToken = bridgeToken;
    }
}

/// @title - A Chainlink CCIP bridge for Arcas between ETH and BNB chain.
/// The idea is same single deployment on both chains, allowing you to then set an immutable mirror for the bridge
contract Bridge is Ownable, CCIPReceiver, ReentrancyGuard {

    /////////////////////////////////////////////////////////
    //                                                     //
    //                      ERRORS                         //
    //                                                     //
    /////////////////////////////////////////////////////////

    error InsufficientWETH();
    error InvalidChainSelector();
    error InvalidSender();
    error BridgeLocked();
    error CannotSendToBurnAddress();
    error InvalidAmount();
    error InsufficientTokenBalance();
    error InsufficientFeePayment();

    /////////////////////////////////////////////////////////
    //                                                     //
    //                      PARAMETERS                     //
    //                                                     //            
    /////////////////////////////////////////////////////////

    // Bridge only for a single chain
    uint64 private immutable allowedChainSelector;
    // Arcas token to be bridged
    IERC20 public immutable arcas;
    // Chainlink CCIP Router 
    IRouterClient private immutable s_router;
    // Native wrapped token
    IWETH public immutable weth;
    // Lock the bridge
    bool public lock;

    /////////////////////////////////////////////////////////
    //                                                     //
    //                      CONSTRUCTOR                    //
    //                                                     //            
    ///////////////////////////////////////////////////////// 

    /// @notice Constructor initializes the contract with the router address.
    constructor(
        address _configuration
        )
    Ownable()
    CCIPReceiver(CCIPBridgeConfiguration(_configuration).getRouter())
     {
        (
            address _router, 
            address _weth, 
            uint64 _destinationChainSelector, 
            address _bridgeToken
        ) = CCIPBridgeConfiguration(_configuration).getConfiguration();

        s_router = IRouterClient(_router);
        weth = IWETH(_weth);
        allowedChainSelector = _destinationChainSelector;
        arcas = IERC20(_bridgeToken);
        lock = true;
        _initializeOwner(CCIPBridgeConfiguration(_configuration).owner());

        weth.approve(_router, type(uint256).max);
    }

    /////////////////////////////////////////////////////////
    //                                                     //
    //                 ADMIN CONTROLS                      //
    //                                                     //        
    /////////////////////////////////////////////////////////

    /// @notice Toggle lock for users using the bridge function
    function toggleLock() external onlyOwner {
        lock = !lock;   
    }

    /// @notice Allows the owner to extract additional WETH in contract as fees.
    function withdrawWeth(
        uint256 _amount,
        address _recipient
    ) external onlyOwner {
        if(weth.balanceOf(address(this)) < _amount) revert InsufficientWETH();

        weth.transfer(_recipient, _amount);
    }
    

    /// @notice Sends data to receiver on the destination chain.
    /// @dev Pays the gas fee with msg.value and wraps it to WETH
    /// @param _amount The uint amount to be sent.
    /// @param _tokenRecipient The address to send token to.
    /// @return messageId The ID of the message that was sent.
    function bridge(
        uint256 _amount,
        address _tokenRecipient
    ) external payable nonReentrant returns (bytes32 messageId) {

        //Ensure bridge is open for use
        if (lock) revert BridgeLocked();
        //Ensure recipient isn't burn
        if (_tokenRecipient == address(0)) revert CannotSendToBurnAddress();
        //Ensure amount is greater than 0
        if (_amount == 0) revert InvalidAmount();

        // Create an EVM2AnyMessage struct in memory with necessary information for sending a cross-chain message
        Client.EVM2AnyMessage memory evm2AnyMessage = Client.EVM2AnyMessage({
            receiver: abi.encode(address(this)), // ABI-encoded receiver address
            data: abi.encode(_amount, _tokenRecipient), // ABI-encoded number and recipient for transfer
            tokenAmounts: new Client.EVMTokenAmount[](0), // Empty array indicating no tokens are being sent
            extraArgs: Client._argsToBytes(
                // Additional arguments, setting gas limit
                Client.EVMExtraArgsV1({gasLimit: 130_000})
            ),
            // Set the feeToken  address, indicating native wrapped WETH will be used for fees
            feeToken: address(weth)
        });

        // Transfers the tokens into the bridge
        arcas.transferFrom(msg.sender, address(this), _amount);

        // Cache balance of wrapped native before message send
        uint256 wrappedNativeBalanceBefore = weth.balanceOf(address(this));

        // Do wrap of msg.value to wrapped native
        weth.deposit{value: msg.value}();

        // Send the message through the router and store the returned message ID
        messageId = s_router.ccipSend(allowedChainSelector, evm2AnyMessage);

        // Revert if message send consumed more wrapped native than was supplied
        if (wrappedNativeBalanceBefore > weth.balanceOf(address(this))) {
            revert InsufficientFeePayment();
        }

        // Return the message ID
        return messageId;
    }


    // Function for frontend to estimate fee required for bridge in native tokens.
    function getFee(
        uint256 _amount,
        address _tokenRecipient
    ) external view returns (uint256 fee) {
        // Create an EVM2AnyMessage struct in memory with necessary information for sending a cross-chain message
        Client.EVM2AnyMessage memory evm2AnyMessage = Client.EVM2AnyMessage({
            receiver: abi.encode(address(this)), // ABI-encoded receiver address
            data: abi.encode(_amount, _tokenRecipient), // ABI-encoded number
            tokenAmounts: new Client.EVMTokenAmount[](0), // Empty array indicating no tokens are being sent
            extraArgs: Client._argsToBytes(
                // Additional arguments, setting gas limit
                Client.EVMExtraArgsV1({gasLimit: 130_000})
            ),
            // Set the feeToken  address, indicating native wrapped WETH will be used for fees
            feeToken: address(weth)
        });

        // Get the fee required to send the message
        fee = s_router.getFee(
            allowedChainSelector,
            evm2AnyMessage
        );
    }

    /// Handles a CCIP received message and bridges the tokens
    function _ccipReceive(
        Client.Any2EVMMessage memory any2EvmMessage
    ) internal nonReentrant override {
        
        //Ensure the chain is correct
        if (any2EvmMessage.sourceChainSelector != allowedChainSelector) revert InvalidChainSelector();
        //Ensure the sender is the mirror
        if (abi.decode(any2EvmMessage.sender, (address)) != address(this)) revert InvalidSender();
        //Ensure the bridge is not locked
        if (lock) revert BridgeLocked();

        // abi-decoding of the sent token amount for bridging
        (uint256 amount, address tokenRecipient) = abi.decode(any2EvmMessage.data, (uint256, address)); 

        if (arcas.balanceOf(address(this)) < amount) revert InsufficientTokenBalance();

        // Arcas token transfer
        arcas.transfer(tokenRecipient, amount);
    }
}
ReentrancyGuard.sol 77 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}
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);
}
CCIPReceiver.sol 59 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {IAny2EVMMessageReceiver} from "../interfaces/IAny2EVMMessageReceiver.sol";

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

import {IERC165} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/IERC165.sol";

/// @title CCIPReceiver - Base contract for CCIP applications that can receive messages.
abstract contract CCIPReceiver is IAny2EVMMessageReceiver, IERC165 {
  address internal immutable i_ccipRouter;

  constructor(address router) {
    if (router == address(0)) revert InvalidRouter(address(0));
    i_ccipRouter = router;
  }

  /// @notice IERC165 supports an interfaceId
  /// @param interfaceId The interfaceId to check
  /// @return true if the interfaceId is supported
  /// @dev Should indicate whether the contract implements IAny2EVMMessageReceiver
  /// e.g. return interfaceId == type(IAny2EVMMessageReceiver).interfaceId || interfaceId == type(IERC165).interfaceId
  /// This allows CCIP to check if ccipReceive is available before calling it.
  /// If this returns false or reverts, only tokens are transferred to the receiver.
  /// If this returns true, tokens are transferred and ccipReceive is called atomically.
  /// Additionally, if the receiver address does not have code associated with
  /// it at the time of execution (EXTCODESIZE returns 0), only tokens will be transferred.
  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
    return interfaceId == type(IAny2EVMMessageReceiver).interfaceId || interfaceId == type(IERC165).interfaceId;
  }

  /// @inheritdoc IAny2EVMMessageReceiver
  function ccipReceive(Client.Any2EVMMessage calldata message) external virtual override onlyRouter {
    _ccipReceive(message);
  }

  /// @notice Override this function in your implementation.
  /// @param message Any2EVMMessage
  function _ccipReceive(Client.Any2EVMMessage memory message) internal virtual;

  /////////////////////////////////////////////////////////////////////
  // Plumbing
  /////////////////////////////////////////////////////////////////////

  /// @notice Return the current router
  /// @return CCIP router address
  function getRouter() public view virtual returns (address) {
    return address(i_ccipRouter);
  }

  error InvalidRouter(address router);

  /// @dev only calls from the set router are accepted.
  modifier onlyRouter() {
    if (msg.sender != getRouter()) revert InvalidRouter(msg.sender);
    _;
  }
}
Client.sol 55 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// End consumer library.
library Client {
  /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
  struct EVMTokenAmount {
    address token; // token address on the local chain.
    uint256 amount; // Amount of tokens.
  }

  struct Any2EVMMessage {
    bytes32 messageId; // MessageId corresponding to ccipSend on source.
    uint64 sourceChainSelector; // Source chain selector.
    bytes sender; // abi.decode(sender) if coming from an EVM chain.
    bytes data; // payload sent in original message.
    EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.
  }

  // If extraArgs is empty bytes, the default is 200k gas limit.
  struct EVM2AnyMessage {
    bytes receiver; // abi.encode(receiver address) for dest EVM chains
    bytes data; // Data payload
    EVMTokenAmount[] tokenAmounts; // Token transfers
    address feeToken; // Address of feeToken. address(0) means you will send msg.value.
    bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV2)
  }

  // bytes4(keccak256("CCIP EVMExtraArgsV1"));
  bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;

  struct EVMExtraArgsV1 {
    uint256 gasLimit;
  }

  function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {
    return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);
  }

  // bytes4(keccak256("CCIP EVMExtraArgsV2"));
  bytes4 public constant EVM_EXTRA_ARGS_V2_TAG = 0x181dcf10;

  /// @param gasLimit: gas limit for the callback on the destination chain.
  /// @param allowOutOfOrderExecution: if true, it indicates that the message can be executed in any order relative to other messages from the same sender.
  /// This value's default varies by chain. On some chains, a particular value is enforced, meaning if the expected value
  /// is not set, the message request will revert.
  struct EVMExtraArgsV2 {
    uint256 gasLimit;
    bool allowOutOfOrderExecution;
  }

  function _argsToBytes(EVMExtraArgsV2 memory extraArgs) internal pure returns (bytes memory bts) {
    return abi.encodeWithSelector(EVM_EXTRA_ARGS_V2_TAG, extraArgs);
  }
}
Ownable.sol 278 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev Cannot double-initialize.
    error AlreadyInitialized();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The owner slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    bytes32 internal constant _OWNER_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
    function _guardInitializeOwner() internal pure virtual returns (bool guard) {}

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                if sload(ownerSlot) {
                    mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                    revert(0x1c, 0x04)
                }
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(_OWNER_SLOT, newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_OWNER_SLOT)
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}
IRouterClient.sol 37 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

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

interface IRouterClient {
  error UnsupportedDestinationChain(uint64 destChainSelector);
  error InsufficientFeeTokenAmount();
  error InvalidMsgValue();

  /// @notice Checks if the given chain ID is supported for sending/receiving.
  /// @param destChainSelector The chain to check.
  /// @return supported is true if it is supported, false if not.
  function isChainSupported(uint64 destChainSelector) external view returns (bool supported);

  /// @param destinationChainSelector The destination chainSelector
  /// @param message The cross-chain CCIP message including data and/or tokens
  /// @return fee returns execution fee for the message
  /// delivery to destination chain, denominated in the feeToken specified in the message.
  /// @dev Reverts with appropriate reason upon invalid message.
  function getFee(
    uint64 destinationChainSelector,
    Client.EVM2AnyMessage memory message
  ) external view returns (uint256 fee);

  /// @notice Request a message to be sent to the destination chain
  /// @param destinationChainSelector The destination chain ID
  /// @param message The cross-chain CCIP message including data and/or tokens
  /// @return messageId The message ID
  /// @dev Note if msg.value is larger than the required fee (from getFee) we accept
  /// the overpayment with no refund.
  /// @dev Reverts with appropriate reason upon invalid message.
  function ccipSend(
    uint64 destinationChainSelector,
    Client.EVM2AnyMessage calldata message
  ) external payable returns (bytes32);
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
IAny2EVMMessageReceiver.sol 15 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

/// @notice Application contracts that intend to receive messages from
/// the router should implement this interface.
interface IAny2EVMMessageReceiver {
  /// @notice Called by the Router to deliver a message.
  /// If this reverts, any token transfers also revert. The message
  /// will move to a FAILED state and become available for manual execution.
  /// @param message CCIP Message
  /// @dev Note ensure you check the msg.sender is the OffRampRouter
  function ccipReceive(Client.Any2EVMMessage calldata message) external;
}

Read Contract

arcas 0x236e7cb4 → address
getFee 0xdfcf048d → uint256
getRouter 0xb0f479a1 → address
lock 0xf83d08ba → bool
owner 0x8da5cb5b → address
ownershipHandoverExpiresAt 0xfee81cf4 → uint256
supportsInterface 0x01ffc9a7 → bool
weth 0x3fc8cef3 → address

Write Contract 9 functions

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

bridge 0x9394d2e8
uint256 _amount
address _tokenRecipient
returns: bytes32
cancelOwnershipHandover 0x54d1f13d
No parameters
ccipReceive 0xb503dfa3
tuple message
completeOwnershipHandover 0xf04e283e
address pendingOwner
renounceOwnership 0x715018a6
No parameters
requestOwnershipHandover 0x25692962
No parameters
toggleLock 0xff9413d8
No parameters
transferOwnership 0xf2fde38b
address newOwner
withdrawWeth 0x02a0ba3f
uint256 _amount
address _recipient

Token Balances (1)

View Transfers →
WETH 0.001

Recent Transactions

No transactions found for this address