Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
BtcMirror
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "./Endian.sol";
import "./interfaces/IBtcMirror.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
//
// #
// # #
// # # #
// # # # #
// # # # # #
// # # # # # #
// # # # # # # #
// # # # # # # # #
// # # # # # # # # #
// # # # # # # # # # #
// # # # # # # # # # # #
// # # # # # #
// + # +
// ++++ ++++
// ++++++ ++++++
// +++++++++
// +++++
// +
//
// BtcMirror lets you prove that a Bitcoin transaction executed, on Ethereum. It
// does this by running an on-chain light client.
//
// Anyone can submit block headers to BtcMirror. The contract verifies
// proof-of-work, keeping only the longest chain it has seen. As long as 50% of
// Bitcoin hash power is honest and at least one person is running the submitter
// script, the BtcMirror contract always reports the current canonical Bitcoin
// chain.
contract BtcMirror is IBtcMirror, OwnableUpgradeable {
/**
* @notice Emitted whenever the contract accepts a new heaviest chain.
*/
event NewTip(uint256 blockHeight, uint256 blockTime, bytes32 blockHash);
/**
* @notice Emitted only after a difficulty retarget, when the contract
* accepts a new heaviest chain with updated difficulty.
*/
event NewTotalDifficultySinceRetarget(uint256 blockHeight, uint256 totalDifficulty, uint32 newDifficultyBits);
/**
* @notice Emitted when we reorg out a portion of the chain.
*/
event Reorg(uint256 count, bytes32 oldTip, bytes32 newTip);
uint256 private latestBlockHeight;
uint256 private latestBlockTime;
mapping(uint256 => bytes32) private blockHeightToHash;
/**
* @notice Difficulty targets in each retargeting period.
*/
mapping(uint256 => uint256) public periodToTarget;
/**
* @notice Whether we're tracking testnet or mainnet Bitcoin.
*/
bool public isTestnet;
/**
* @notice Constructor for the logic contract
* @dev Disables initializers to prevent direct initialization of the logic contract.
* This is a security measure for upgradeable contracts deployed behind proxies.
* The logic contract should only be initialized through the proxy.
* @custom:oz-upgrades-unsafe-allow constructor
*/
constructor() {
_disableInitializers();
}
/**
* @notice Initializes the contract.
* Tracks Bitcoin starting from a given block. The isTestnet
* argument is necessary because the Bitcoin testnet does not
* respect the difficulty rules, so we disable block difficulty
* checks in order to track it.
*/
function initialize(
address _admin,
uint256 _blockHeight,
bytes32 _blockHash,
uint256 _blockTime,
uint256 _expectedTarget,
bool _isTestnet
) external initializer {
require(_admin != address(0), "Invalid admin address");
__Ownable_init(_admin);
blockHeightToHash[_blockHeight] = _blockHash;
latestBlockHeight = _blockHeight;
latestBlockTime = _blockTime;
periodToTarget[_blockHeight / 2016] = _expectedTarget;
isTestnet = _isTestnet;
}
/**
* @notice Returns the Bitcoin block hash at a specific height.
*/
function getBlockHash(uint256 number) public view returns (bytes32) {
return blockHeightToHash[number];
}
/**
* @notice Returns the height of the current chain tip.
*/
function getLatestBlockHeight() public view returns (uint256) {
return latestBlockHeight;
}
/**
* @notice Returns the timestamp of the current chain tip.
*/
function getLatestBlockTime() public view returns (uint256) {
return latestBlockTime;
}
/**
* @notice Allows the owner to submit a new Bitcoin chain segment.
* This function is only callable by the owner.
* @dev This function is used to submit new blocks to the BtcMirror contract.
* It verifies the block headers and updates the state accordingly.
*/
function submit_uncheck(uint256 blockHeight, bytes calldata blockHeaders, uint8 v, bytes32 r, bytes32 s)
external
onlyOwner
{
bytes32 hash = keccak256(abi.encode(blockHeight, blockHeaders));
address expected_addr = ecrecover(hash, v, r, s);
require(expected_addr == msg.sender, "Invalid signature");
uint256 numHeaders = blockHeaders.length / 80;
require(numHeaders * 80 == blockHeaders.length, "wrong header length");
require(numHeaders > 0, "must submit at least one block");
uint256 newHeight = blockHeight + numHeaders - 1;
for (uint256 i = 0; i < numHeaders; i++) {
bytes calldata blockHeader = blockHeaders[80 * i:80 * (i + 1)];
uint256 blockNum = blockHeight + i;
uint256 blockHashNum = Endian.reverse256(uint256(sha256(abi.encode(sha256(blockHeader)))));
blockHeightToHash[blockNum] = bytes32(blockHashNum);
if (blockNum % 2016 == 0) {
bytes32 bits = bytes32(blockHeader[72:76]);
uint256 target = getTarget(bits);
periodToTarget[blockNum / 2016] = target;
}
}
latestBlockHeight = newHeight;
latestBlockTime =
Endian.reverse32(uint32(bytes4(blockHeaders[blockHeaders.length - 12:blockHeaders.length - 8])));
bytes32 newTip = getBlockHash(newHeight);
emit NewTip(newHeight, latestBlockTime, newTip);
}
/**
* @notice Validates a Bitcoin block header
* @param header 80-byte block header data
* @param blockHeight block height
* @return true if the block header is valid, false otherwise
*/
function validateHeader(bytes calldata header, uint256 blockHeight) public view returns (bool) {
require(header.length == 80, "wrong header length");
require(blockHeight > 0, "The genesis block cannot be validated");
// 1. Validate block hash calculation
uint256 blockHashNum = Endian.reverse256(uint256(sha256(abi.encode(sha256(header)))));
// 2. Validate previous block hash
bytes32 prevHash = bytes32(Endian.reverse256(uint256(bytes32(header[4:36]))));
// Check if previous block exists
if (blockHeightToHash[blockHeight - 1] == bytes32(0)) {
return false;
}
// Check if previous block hash matches
if (prevHash != blockHeightToHash[blockHeight - 1]) {
return false;
}
// 3. Validate proof of work and difficulty target
bytes32 bits = bytes32(header[72:76]);
uint256 target = getTarget(bits);
// 4. Validate difficulty target
uint256 period = blockHeight / 2016;
if (blockHeight % 2016 == 0) {
// For difficulty adjustment blocks, validate the adjustment is legal
uint256 lastTarget = periodToTarget[period - 1];
if (!isTestnet && target >> 2 >= lastTarget) {
// Difficulty decreased by more than 75%
return false;
}
} else if (!isTestnet && target != periodToTarget[period]) {
// For non-adjustment blocks, validate difficulty matches current period
return false;
}
// 5. Validate block hash meets difficulty requirement
if (blockHashNum >= target) {
return false;
}
// 6. Validate version number
uint32 version = Endian.reverse32(uint32(bytes4(header[0:4])));
if (version < 2) {
return false;
}
// 7. Validate Merkle Root
bytes32 merkleRoot = bytes32(Endian.reverse256(uint256(bytes32(header[36:68]))));
if (merkleRoot == bytes32(0)) {
return false;
}
return true;
}
/**
* @notice Challenges a previously submitted block by proving it's invalid, and submit the correct chain.
* @dev This function allows anyone to challenge a block by providing evidence that it violates Bitcoin consensus rules.
* The challenger must provide:
* 1. The owner's signature to prevent spam
* 2. The invalid block headers
* 3. The index of the specific invalid block
* 4. A valid alternative chain
* If the challenge is successful (i.e., the block is proven invalid), the alternative chain is accepted.
*
* @param wrong_idx Index of the invalid block in the wrongBlockHeaders array
* @param wrongBlockHeaders Array of block headers containing the invalid block
* @param v Recovery byte of the owner's signature
* @param r R component of the owner's signature
* @param s S component of the owner's signature
* @param blockHeight Starting height of the alternative chain
* @param blockHeaders Alternative valid chain to replace the invalid one
*
* @custom:security-note The owner's signature is required to prevent DoS attacks through excessive challenges
*/
function challenge(
uint256 wrong_idx,
bytes calldata wrongBlockHeaders,
uint8 v,
bytes32 r,
bytes32 s,
uint256 blockHeight,
bytes calldata blockHeaders
) external {
address owner = owner();
bytes32 hash = keccak256(abi.encode(blockHeight, wrongBlockHeaders));
address expected_addr = ecrecover(hash, v, r, s);
require(expected_addr == owner, "Challenger use wrong signature");
require(blockHeight > 0, "The genesis block cannot be challenged");
uint256 numWrongHeaders = wrongBlockHeaders.length / 80;
require(numWrongHeaders * 80 == wrongBlockHeaders.length, "wrong header length");
require(numWrongHeaders > 0, "must submit at least one block");
bytes calldata wrongHeader = wrongBlockHeaders[80 * wrong_idx:80 * (wrong_idx + 1)];
uint256 wrongBlockHeight = blockHeight + wrong_idx;
// Validate the challenged block header
bool isValid = validateHeader(wrongHeader, wrongBlockHeight);
require(!isValid, "The challenged block appears to be valid");
// submit the correct chain
submit(blockHeight, blockHeaders);
}
/**
* Submits a new Bitcoin chain segment. Must be heavier (not necessarily
* longer) than the chain rooted at getBlockHash(getLatestBlockHeight()).
*/
function submit(uint256 blockHeight, bytes calldata blockHeaders) private {
uint256 numHeaders = blockHeaders.length / 80;
require(numHeaders * 80 == blockHeaders.length, "wrong header length");
require(numHeaders > 0, "must submit at least one block");
// sanity check: the new chain must not end in a past difficulty period
// (BtcMirror does not support a 2-week reorg)
uint256 oldPeriod = latestBlockHeight / 2016;
uint256 newHeight = blockHeight + numHeaders - 1;
uint256 newPeriod = newHeight / 2016;
require(newPeriod >= oldPeriod, "old difficulty period");
// if we crossed a retarget, do extra math to compare chain weight
uint256 parentPeriod = (blockHeight - 1) / 2016;
uint256 oldWork = 0;
if (newPeriod > parentPeriod) {
assert(newPeriod == parentPeriod + 1);
// the submitted chain segment contains a difficulty retarget.
if (newPeriod == oldPeriod) {
// the old canonical chain is past the retarget
// we cannot compare length, we must compare total work
oldWork = getWorkInPeriod(oldPeriod, latestBlockHeight);
} else {
// the old canonical chain is before the retarget
assert(oldPeriod == parentPeriod);
}
}
// verify and store each block
bytes32 oldTip = getBlockHash(latestBlockHeight);
uint256 nReorg = 0;
for (uint256 i = 0; i < numHeaders; i++) {
uint256 blockNum = blockHeight + i;
nReorg += submitBlock(blockNum, blockHeaders[80 * i:80 * (i + 1)]);
}
// check that we have a new heaviest chain
if (newPeriod > parentPeriod) {
// the submitted chain segment crosses into a new difficulty
// period. this is happens once every ~2 weeks. check total work
bytes calldata lastHeader = blockHeaders[80 * (numHeaders - 1):];
uint32 newDifficultyBits = Endian.reverse32(uint32(bytes4(lastHeader[72:76])));
uint256 newWork = getWorkInPeriod(newPeriod, newHeight);
require(newWork > oldWork, "insufficient total difficulty");
// erase any block hashes above newHeight, now invalidated.
// (in case we just accepted a shorter, heavier chain.)
for (uint256 i = newHeight + 1; i <= latestBlockHeight; i++) {
blockHeightToHash[i] = 0;
}
emit NewTotalDifficultySinceRetarget(newHeight, newWork, newDifficultyBits);
} else {
// here we know what newPeriod == oldPeriod == parentPeriod
// with identical per-block difficulty. just keep the longest chain.
assert(newPeriod == oldPeriod);
assert(newPeriod == parentPeriod);
require(newHeight > latestBlockHeight, "insufficient chain length");
}
// record the new tip height and timestamp
latestBlockHeight = newHeight;
uint256 ixT = blockHeaders.length - 12;
uint32 time = uint32(bytes4(blockHeaders[ixT:ixT + 4]));
latestBlockTime = Endian.reverse32(time);
// finally, log the new tip
bytes32 newTip = getBlockHash(newHeight);
emit NewTip(newHeight, latestBlockTime, newTip);
if (nReorg > 0) {
emit Reorg(nReorg, oldTip, newTip);
}
}
function getWorkInPeriod(uint256 period, uint256 height) private view returns (uint256) {
uint256 target = periodToTarget[period];
uint256 workPerBlock = (2 ** 256 - 1) / target;
uint256 numBlocks = height - (period * 2016) + 1;
assert(numBlocks >= 1 && numBlocks <= 2016);
return numBlocks * workPerBlock;
}
function submitBlock(uint256 blockHeight, bytes calldata blockHeader) private returns (uint256 numReorged) {
// compute the block hash
assert(blockHeader.length == 80);
uint256 blockHashNum = Endian.reverse256(uint256(sha256(abi.encode(sha256(blockHeader)))));
// optimistically save the block hash
// we'll revert if the header turns out to be invalid
bytes32 oldHash = blockHeightToHash[blockHeight];
bytes32 newHash = bytes32(blockHashNum);
if (oldHash != bytes32(0) && oldHash != newHash) {
// if we're overwriting a non-zero block hash, that block is reorged
numReorged = 1;
}
// this is the most expensive line. 20,000 gas to use a new storage slot
blockHeightToHash[blockHeight] = newHash;
// verify previous hash
bytes32 prevHash = bytes32(Endian.reverse256(uint256(bytes32(blockHeader[4:36]))));
require(prevHash == blockHeightToHash[blockHeight - 1], "bad parent");
require(prevHash != bytes32(0), "parent block not yet submitted");
// verify proof-of-work
bytes32 bits = bytes32(blockHeader[72:76]);
uint256 target = getTarget(bits);
require(blockHashNum < target, "block hash above target");
// support once-every-2016-blocks retargeting
uint256 period = blockHeight / 2016;
if (blockHeight % 2016 == 0) {
// Bitcoin enforces a minimum difficulty of 25% of the previous
// difficulty. Doing the full calculation here does not necessarily
// add any security. We keep the heaviest chain, not the longest.
uint256 lastTarget = periodToTarget[period - 1];
// ignore difficulty update rules on testnet.
// Bitcoin testnet has some clown hacks regarding difficulty, see
// https://blog.lopp.net/the-block-storms-of-bitcoins-testnet/
if (!isTestnet) {
require(target >> 2 < lastTarget, "<25% difficulty retarget");
}
periodToTarget[period] = target;
} else if (!isTestnet) {
// verify difficulty
require(target == periodToTarget[period], "wrong difficulty bits");
}
}
function getTarget(bytes32 bits) public pure returns (uint256) {
// Bitcoin represents difficulty using a custom floating-point big int
// representation. the "difficulty bits" consist of an 8-bit exponent
// and a 24-bit mantissa, which combine to generate a u256 target. the
// block hash must be below the target.
uint256 exp = uint8(bits[3]);
uint256 mantissa = uint8(bits[2]);
mantissa = (mantissa << 8) | uint8(bits[1]);
mantissa = (mantissa << 8) | uint8(bits[0]);
uint256 target = mantissa << (8 * (exp - 3));
return target;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
// Bitwise math helpers for dealing with Bitcoin block headers.
// Bitcoin block fields are little-endian. Must flip to big-endian for EVM.
library Endian {
function reverse256(uint256 input) internal pure returns (uint256 v) {
v = input;
// swap bytes
uint256 pat1 = 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00;
v = ((v & pat1) >> 8) | ((v & ~pat1) << 8);
// swap 2-byte long pairs
uint256 pat2 = 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000;
v = ((v & pat2) >> 16) | ((v & ~pat2) << 16);
// swap 4-byte long pairs
uint256 pat4 = 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000;
v = ((v & pat4) >> 32) | ((v & ~pat4) << 32);
// swap 8-byte long pairs
uint256 pat8 = 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000;
v = ((v & pat8) >> 64) | ((v & ~pat8) << 64);
// swap 16-byte long pairs
v = (v >> 128) | (v << 128);
}
function reverse64(uint64 input) internal pure returns (uint64 v) {
v = input;
// swap bytes
v = ((v & 0xFF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF) << 8);
// swap 2-byte long pairs
v = ((v & 0xFFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF) << 16);
// swap 4-byte long pairs
v = (v >> 32) | (v << 32);
}
function reverse32(uint32 input) internal pure returns (uint32 v) {
v = input;
// swap bytes
v = ((v & 0xFF00FF00) >> 8) | ((v & 0x00FF00FF) << 8);
// swap 2-byte long pairs
v = (v >> 16) | (v << 16);
}
function reverse16(uint16 input) internal pure returns (uint16 v) {
v = input;
// swap bytes
v = (v >> 8) | (v << 8);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
/**
* @notice Tracks Bitcoin. Provides block hashes.
*/
interface IBtcMirror {
/**
* @notice Returns the Bitcoin block hash at a specific height.
*/
function getBlockHash(uint256 number) external view returns (bytes32);
/**
* @notice Returns the height of the latest block (tip of the chain).
*/
function getLatestBlockHeight() external view returns (uint256);
/**
* @notice Returns the timestamp of the lastest block, as Unix seconds.
*/
function getLatestBlockTime() external view returns (uint256);
/**
* @notice Submits a new Bitcoin chain segment (80-byte headers) s
*/
function submit_uncheck(uint256 blockHeight, bytes calldata blockHeaders, uint8 v, bytes32 r, bytes32 s) external;
/**
* @notice Challenges a previously submitted block by proving it's invalid, and submit the correct chain.
*/
function challenge(
uint256 wrong_idx,
bytes calldata wrongBlockHeaders,
uint8 v,
bytes32 r,
bytes32 s,
uint256 blockHeight,
bytes calldata blockHeaders
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reinitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.
*
* NOTE: Consider following the ERC-7201 formula to derive storage locations.
*/
function _initializableStorageSlot() internal pure virtual returns (bytes32) {
return INITIALIZABLE_STORAGE;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
bytes32 slot = _initializableStorageSlot();
assembly {
$.slot := slot
}
}
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@btc-light-client/packages/contracts/=lib/btc-light-client/packages/contracts/",
"@openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/",
"btc-light-client/=lib/btc-light-client/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"blockHeight","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockTime","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"name":"NewTip","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"blockHeight","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalDifficulty","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"newDifficultyBits","type":"uint32"}],"name":"NewTotalDifficultySinceRetarget","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"oldTip","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"newTip","type":"bytes32"}],"name":"Reorg","type":"event"},{"inputs":[{"internalType":"uint256","name":"wrong_idx","type":"uint256"},{"internalType":"bytes","name":"wrongBlockHeaders","type":"bytes"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint256","name":"blockHeight","type":"uint256"},{"internalType":"bytes","name":"blockHeaders","type":"bytes"}],"name":"challenge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"number","type":"uint256"}],"name":"getBlockHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLatestBlockHeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLatestBlockTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"bits","type":"bytes32"}],"name":"getTarget","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"},{"internalType":"uint256","name":"_blockHeight","type":"uint256"},{"internalType":"bytes32","name":"_blockHash","type":"bytes32"},{"internalType":"uint256","name":"_blockTime","type":"uint256"},{"internalType":"uint256","name":"_expectedTarget","type":"uint256"},{"internalType":"bool","name":"_isTestnet","type":"bool"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isTestnet","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"periodToTarget","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockHeight","type":"uint256"},{"internalType":"bytes","name":"blockHeaders","type":"bytes"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"submit_uncheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"header","type":"bytes"},{"internalType":"uint256","name":"blockHeight","type":"uint256"}],"name":"validateHeader","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6080806040523460aa575f5160206117f85f395f51905f525460ff8160401c16609b576002600160401b03196001600160401b038216016049575b60405161174990816100af8239f35b6001600160401b0319166001600160401b039081175f5160206117f85f395f51905f525581527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80603a565b63f92ee8a960e01b5f5260045ffd5b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c9081630d2b2860146110335750806332c258321461101657806336e6393614610fcd5780635d69c83f146106f7578063715018a61461069057806376c88686146104af5780638159e39b1461019c5780638da5cb5b1461016857806392108c8614610146578063a9c2d7ab14610120578063e875aa5d14610104578063ee82ac5e146100da5763f2fde38b146100ab575f80fd5b346100d65760203660031901126100d6576100d46100c7611097565b6100cf6115ed565b61144f565b005b5f80fd5b346100d65760203660031901126100d6576004355f526002602052602060405f2054604051908152f35b346100d6575f3660031901126100d65760205f54604051908152f35b346100d65760203660031901126100d657602061013e600435611412565b604051908152f35b346100d6575f3660031901126100d657602060ff600454166040519015158152f35b346100d6575f3660031901126100d6575f5160206116d45f395f51905f52546040516001600160a01b039091168152602090f35b346100d65760a03660031901126100d65760043560243567ffffffffffffffff81116100d6576101d0903690600401611059565b60205f6101de949394611087565b6101e66115ed565b6040518381019061020b816101fd888b8b8761138c565b03601f1981018352826110ef565b5190206040805191825260ff92909216602082015260643591810191909152608435606082015281805260809060015afa1561046b575f51336001600160a01b039091160361047657605081049160508302831584820460501481171561035f5761027a8461028093146110ad565b156113b9565b61028a8382611405565b5f1981019390841161035f575f5b81811061037357505050815f55600b19810181811161035f5760071982019180831161035f576102f663ffffffff93610327936102fc937f11cedc7f83a9a6f5228b8fcd3c3c0ae686a51e2e04ea46923e3491a32a4bbaa898611125565b9061113d565b60e01c62ff00ff63ff00ff008260081b169160081c161763ffffffff808260101b169160101c161790565b169081600155805f52600260205261035a60405f2054604051938493846040919493926060820195825260208201520152565b0390a1005b634e487b7160e01b5f52601160045260245ffd5b8060500260508104820361035f576001820180831161035f578060500290605082040361035f576103a5918689611125565b6103b28386949394611405565b9060205f6040518387823780848101838152039060025afa1561046b5760205f80516040518381019182528381526103eb6040826110ef565b604051918291518091835e8101838152039060025afa1561046b576104105f516114c0565b825f52600260205260405f20556107e0820615610434575b50506001915001610298565b604c116100d6576107e06104566048600195013563ffffffff60e01b16611412565b91045f52600360205260405f20558780610428565b6040513d5f823e3d90fd5b60405162461bcd60e51b8152602060048201526011602482015270496e76616c6964207369676e617475726560781b6044820152606490fd5b346100d65760c03660031901126100d6576104c8611097565b6024359060a435908115158092036100d6575f5160206116f45f395f51905f52549260ff8460401c16159367ffffffffffffffff811680159081610688575b600114908161067e575b159081610675575b506106665767ffffffffffffffff1981166001175f5160206116f45f395f51905f52558461063a575b506001600160a01b038216156105fd576105696107e0926105616116a8565b6100cf6116a8565b805f52600260205260443560405f2055805f55606435600155045f52600360205260843560405f205560ff8019600454169116176004556105a657005b68ff0000000000000000195f5160206116f45f395f51905f5254165f5160206116f45f395f51905f52557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b60405162461bcd60e51b8152602060048201526015602482015274496e76616c69642061646d696e206164647265737360581b6044820152606490fd5b68ffffffffffffffffff191668010000000000000001175f5160206116f45f395f51905f525584610542565b63f92ee8a960e01b5f5260045ffd5b90501586610519565b303b159150610511565b869150610507565b346100d6575f3660031901126100d6576106a86115ed565b5f5160206116d45f395f51905f5280546001600160a01b031981169091555f906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346100d65760e03660031901126100d65760043560243567ffffffffffffffff81116100d65761072b903690600401611059565b90610734611087565b9060a4359360c43567ffffffffffffffff81116100d657610759903690600401611059565b94909360205f60018060a01b035f5160206116d45f395f51905f525416928960405161078e816101fd898c898401968761138c565b5190206040805191825260ff92909216602082015260643591810191909152608435606082015281805260809060015afa1561046b575f516001600160a01b031603610f88578515610f3457605081046050810290801590820460501481171561035f5761027a8361080093146110ad565b816050029260508404830361035f576001830180841161035f578060500290605082040361035f57610848946108429361083993611125565b91909287611405565b91611173565b610ede57605082049260508402841585820460501481171561035f5761027a8561087293146110ad565b5f54916107e08304936108858684611405565b5f198101959080871161035f576107e0870497828910610ea1575f19860186811161035f576107e090045f93818b119889610e61575b5f52600260205260405f2054985f985f905b858210610b3b57505015610ace5750505f19810190811161035f578060500290605082040361035f5783610902918187611125565b604c116100d6578761094663ffffffff604861094c94013560e01c1662ff00ff63ff00ff008260081b169160081c161763ffffffff808260101b169160101c161790565b99611620565b91821115610a89575b5f54811161097b575f818152600260205260408120555f19811461035f57600101610955565b5060609063ffffffff7f452ea9c9e4985cb8cc7199131fd292d600dae30f407e749b6ca7e7f4c7690cf7939495969798604051928a84526020840152166040820152a15b845f55600b19810181811161035f5760071982019182821161035f576102f663ffffffff93610a13936102fc937f11cedc7f83a9a6f5228b8fcd3c3c0ae686a51e2e04ea46923e3491a32a4bbaa897611125565b1680600155845f52600260205260405f205494610a4786604051938493846040919493926060820195825260208201520152565b0390a180610a5157005b7fea952b3dfe43a8e680ea58302c72429cd78ef0537abeaf75797ef9bb0496cefc9260609260405192835260208301526040820152a1005b60405162461bcd60e51b815260206004820152601d60248201527f696e73756666696369656e7420746f74616c20646966666963756c74790000006044820152606490fd5b90935089610aee95969798999a9350610ae89250146115d2565b146115d2565b5f5485116109bf5760405162461bcd60e51b815260206004820152601960248201527f696e73756666696369656e7420636861696e206c656e677468000000000000006044820152606490fd5b9099610b478b83611405565b8b605002605081048d0361035f5760018d01808e1161035f578060500290605082040361035f57610b79918c8e611125565b9290915f92610b8a605086146115d2565b60205f6040518784823780888101838152039060025afa1561046b5760205f8051604051838101918252838152610bc26040826110ef565b604051918291518091835e8101838152039060025afa1561046b57610be75f516114c0565b94825f52600260205260405f2054868115159182610e56575b5050610e4d575b825f5260026020528560405f2055806024116100d657610c2a60048301356114c0565b5f19840184811161035f575f52600260205260405f20548103610e1b5715610dd657604c116100d6576048610c6991013563ffffffff60e01b16611412565b80941015610d91576107e08082049106610d1e575f19810181811161035f575f52600360205260405f205460ff6004541615610cc3575b5090600193610cbb93925f52600360205260405f2055611405565b9a01906108cd565b846002949392941c1015610cd95790915f610ca0565b60405162461bcd60e51b815260206004820152601860248201527f3c32352520646966666963756c747920726574617267657400000000000000006044820152606490fd5b60ff600494929394541615610d3c575b5050600191610cbb91611405565b5f52600360205260405f205403610d54578f80610d2e565b60405162461bcd60e51b815260206004820152601560248201527477726f6e6720646966666963756c7479206269747360581b6044820152606490fd5b60405162461bcd60e51b815260206004820152601760248201527f626c6f636b20686173682061626f7665207461726765740000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601e60248201527f706172656e7420626c6f636b206e6f7420796574207375626d697474656400006044820152606490fd5b60405162461bcd60e51b815260206004820152600a602482015269189859081c185c995b9d60b21b6044820152606490fd5b60019450610c07565b14159050865f610c00565b946001830180841161035f57610e78908d146115d2565b8b8203610e905750610e8a8582611620565b946108bb565b94610e9c8383146115d2565b6108bb565b60405162461bcd60e51b81526020600482015260156024820152741bdb1908191a59999a58dd5b1d1e481c195c9a5bd9605a1b6044820152606490fd5b60405162461bcd60e51b815260206004820152602860248201527f546865206368616c6c656e67656420626c6f636b206170706561727320746f206044820152671899481d985b1a5960c21b6064820152608490fd5b60405162461bcd60e51b815260206004820152602660248201527f5468652067656e6573697320626c6f636b2063616e6e6f74206265206368616c6044820152651b195b99d95960d21b6064820152608490fd5b60405162461bcd60e51b815260206004820152601e60248201527f4368616c6c656e676572207573652077726f6e67207369676e617475726500006044820152606490fd5b346100d65760403660031901126100d65760043567ffffffffffffffff81116100d65761100c6110036020923690600401611059565b60243591611173565b6040519015158152f35b346100d6575f3660031901126100d6576020600154604051908152f35b346100d65760203660031901126100d6576020906004355f526003825260405f20548152f35b9181601f840112156100d65782359167ffffffffffffffff83116100d657602083818601950101116100d657565b6044359060ff821682036100d657565b600435906001600160a01b03821682036100d657565b156110b457565b60405162461bcd60e51b81526020600482015260136024820152720eee4dedcce40d0cac2c8cae440d8cadccee8d606b1b6044820152606490fd5b90601f8019910116810190811067ffffffffffffffff82111761111157604052565b634e487b7160e01b5f52604160045260245ffd5b909392938483116100d65784116100d6578101920390565b356001600160e01b0319811692919060048210611158575050565b6001600160e01b031960049290920360031b82901b16169150565b91611180605083146110ad565b80156113395760205f6040518486823780858101838152039060025afa1561046b5760205f80516040518381019182528381526111be6040826110ef565b604051918291518091835e8101838152039060025afa1561046b576111e35f516114c0565b826024116100d6576111f860048501356114c0565b5f19830183811161035f57805f52600260205260405f20541561132f575f52600260205260405f2054036112e65782604c116100d6576112456001600160e01b0319604886013516611412565b916107e080820491066112fc575f19810190811161035f575f52600360205260405f205460ff600454161590816112ee575b506112e6575b10156112e057806004116100d657600263ffffffff833560e881901c62ff00ff1660d89190911c63ff00ff001617601081811c831691901b82161716106112e0576044116100d65760246112d29101356114c0565b156112dc57600190565b5f90565b50505f90565b505050505f90565b90508260021c10155f611277565b60ff60045416159081611318575b501561127d57505050505f90565b90505f52600360205260405f20548214155f61130a565b5050505050505f90565b60405162461bcd60e51b815260206004820152602560248201527f5468652067656e6573697320626c6f636b2063616e6e6f742062652076616c6960448201526419185d195960da1b6064820152608490fd5b91926060938192845260406020850152816040850152848401375f828201840152601f01601f1916010190565b156113c057565b60405162461bcd60e51b815260206004820152601e60248201527f6d757374207375626d6974206174206c65617374206f6e6520626c6f636b00006044820152606490fd5b9190820180921161035f57565b8060031a90600219820191821161035f576001600160fd1b038216820361035f57805f1a908060011a9060021a60081b1760081b179060031b1b90565b6001600160a01b031680156114ad575f5160206116d45f395f51905f5280546001600160a01b0319811683179091556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b631e4fbdf760e01b5f525f60045260245ffd5b7eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff7fff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff008260081b169160081c16177dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff7fffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff00008260101b169160101c16177bffffffff00000000ffffffff00000000ffffffff00000000ffffffff7fffffffff00000000ffffffff00000000ffffffff00000000ffffffff000000008260201b169160201c161777ffffffffffffffff0000000000000000ffffffffffffffff80198260401b169160401c16178060801b9060801c1790565b156115d957565b634e487b7160e01b5f52600160045260245ffd5b5f5160206116d45f395f51905f52546001600160a01b0316330361160d57565b63118cdaa760e01b5f523360045260245ffd5b90815f52600360205260405f20548015611694575f1904916107e08102908082046107e0149015171561035f57810390811161035f576001810180911161035f57600181101580611688575b611675906115d2565b81810291818304149015171561035f5790565b506107e081111561166c565b634e487b7160e01b5f52601260045260245ffd5b60ff5f5160206116f45f395f51905f525460401c16156116c457565b631afcd79f60e31b5f5260045ffdfe9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220ad19500be3251bf3136e9ebf09bb003a0916620990d4d9ad7693b159667b6fbf64736f6c634300081e0033f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00
Deployed Bytecode
0x6080806040526004361015610012575f80fd5b5f3560e01c9081630d2b2860146110335750806332c258321461101657806336e6393614610fcd5780635d69c83f146106f7578063715018a61461069057806376c88686146104af5780638159e39b1461019c5780638da5cb5b1461016857806392108c8614610146578063a9c2d7ab14610120578063e875aa5d14610104578063ee82ac5e146100da5763f2fde38b146100ab575f80fd5b346100d65760203660031901126100d6576100d46100c7611097565b6100cf6115ed565b61144f565b005b5f80fd5b346100d65760203660031901126100d6576004355f526002602052602060405f2054604051908152f35b346100d6575f3660031901126100d65760205f54604051908152f35b346100d65760203660031901126100d657602061013e600435611412565b604051908152f35b346100d6575f3660031901126100d657602060ff600454166040519015158152f35b346100d6575f3660031901126100d6575f5160206116d45f395f51905f52546040516001600160a01b039091168152602090f35b346100d65760a03660031901126100d65760043560243567ffffffffffffffff81116100d6576101d0903690600401611059565b60205f6101de949394611087565b6101e66115ed565b6040518381019061020b816101fd888b8b8761138c565b03601f1981018352826110ef565b5190206040805191825260ff92909216602082015260643591810191909152608435606082015281805260809060015afa1561046b575f51336001600160a01b039091160361047657605081049160508302831584820460501481171561035f5761027a8461028093146110ad565b156113b9565b61028a8382611405565b5f1981019390841161035f575f5b81811061037357505050815f55600b19810181811161035f5760071982019180831161035f576102f663ffffffff93610327936102fc937f11cedc7f83a9a6f5228b8fcd3c3c0ae686a51e2e04ea46923e3491a32a4bbaa898611125565b9061113d565b60e01c62ff00ff63ff00ff008260081b169160081c161763ffffffff808260101b169160101c161790565b169081600155805f52600260205261035a60405f2054604051938493846040919493926060820195825260208201520152565b0390a1005b634e487b7160e01b5f52601160045260245ffd5b8060500260508104820361035f576001820180831161035f578060500290605082040361035f576103a5918689611125565b6103b28386949394611405565b9060205f6040518387823780848101838152039060025afa1561046b5760205f80516040518381019182528381526103eb6040826110ef565b604051918291518091835e8101838152039060025afa1561046b576104105f516114c0565b825f52600260205260405f20556107e0820615610434575b50506001915001610298565b604c116100d6576107e06104566048600195013563ffffffff60e01b16611412565b91045f52600360205260405f20558780610428565b6040513d5f823e3d90fd5b60405162461bcd60e51b8152602060048201526011602482015270496e76616c6964207369676e617475726560781b6044820152606490fd5b346100d65760c03660031901126100d6576104c8611097565b6024359060a435908115158092036100d6575f5160206116f45f395f51905f52549260ff8460401c16159367ffffffffffffffff811680159081610688575b600114908161067e575b159081610675575b506106665767ffffffffffffffff1981166001175f5160206116f45f395f51905f52558461063a575b506001600160a01b038216156105fd576105696107e0926105616116a8565b6100cf6116a8565b805f52600260205260443560405f2055805f55606435600155045f52600360205260843560405f205560ff8019600454169116176004556105a657005b68ff0000000000000000195f5160206116f45f395f51905f5254165f5160206116f45f395f51905f52557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b60405162461bcd60e51b8152602060048201526015602482015274496e76616c69642061646d696e206164647265737360581b6044820152606490fd5b68ffffffffffffffffff191668010000000000000001175f5160206116f45f395f51905f525584610542565b63f92ee8a960e01b5f5260045ffd5b90501586610519565b303b159150610511565b869150610507565b346100d6575f3660031901126100d6576106a86115ed565b5f5160206116d45f395f51905f5280546001600160a01b031981169091555f906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346100d65760e03660031901126100d65760043560243567ffffffffffffffff81116100d65761072b903690600401611059565b90610734611087565b9060a4359360c43567ffffffffffffffff81116100d657610759903690600401611059565b94909360205f60018060a01b035f5160206116d45f395f51905f525416928960405161078e816101fd898c898401968761138c565b5190206040805191825260ff92909216602082015260643591810191909152608435606082015281805260809060015afa1561046b575f516001600160a01b031603610f88578515610f3457605081046050810290801590820460501481171561035f5761027a8361080093146110ad565b816050029260508404830361035f576001830180841161035f578060500290605082040361035f57610848946108429361083993611125565b91909287611405565b91611173565b610ede57605082049260508402841585820460501481171561035f5761027a8561087293146110ad565b5f54916107e08304936108858684611405565b5f198101959080871161035f576107e0870497828910610ea1575f19860186811161035f576107e090045f93818b119889610e61575b5f52600260205260405f2054985f985f905b858210610b3b57505015610ace5750505f19810190811161035f578060500290605082040361035f5783610902918187611125565b604c116100d6578761094663ffffffff604861094c94013560e01c1662ff00ff63ff00ff008260081b169160081c161763ffffffff808260101b169160101c161790565b99611620565b91821115610a89575b5f54811161097b575f818152600260205260408120555f19811461035f57600101610955565b5060609063ffffffff7f452ea9c9e4985cb8cc7199131fd292d600dae30f407e749b6ca7e7f4c7690cf7939495969798604051928a84526020840152166040820152a15b845f55600b19810181811161035f5760071982019182821161035f576102f663ffffffff93610a13936102fc937f11cedc7f83a9a6f5228b8fcd3c3c0ae686a51e2e04ea46923e3491a32a4bbaa897611125565b1680600155845f52600260205260405f205494610a4786604051938493846040919493926060820195825260208201520152565b0390a180610a5157005b7fea952b3dfe43a8e680ea58302c72429cd78ef0537abeaf75797ef9bb0496cefc9260609260405192835260208301526040820152a1005b60405162461bcd60e51b815260206004820152601d60248201527f696e73756666696369656e7420746f74616c20646966666963756c74790000006044820152606490fd5b90935089610aee95969798999a9350610ae89250146115d2565b146115d2565b5f5485116109bf5760405162461bcd60e51b815260206004820152601960248201527f696e73756666696369656e7420636861696e206c656e677468000000000000006044820152606490fd5b9099610b478b83611405565b8b605002605081048d0361035f5760018d01808e1161035f578060500290605082040361035f57610b79918c8e611125565b9290915f92610b8a605086146115d2565b60205f6040518784823780888101838152039060025afa1561046b5760205f8051604051838101918252838152610bc26040826110ef565b604051918291518091835e8101838152039060025afa1561046b57610be75f516114c0565b94825f52600260205260405f2054868115159182610e56575b5050610e4d575b825f5260026020528560405f2055806024116100d657610c2a60048301356114c0565b5f19840184811161035f575f52600260205260405f20548103610e1b5715610dd657604c116100d6576048610c6991013563ffffffff60e01b16611412565b80941015610d91576107e08082049106610d1e575f19810181811161035f575f52600360205260405f205460ff6004541615610cc3575b5090600193610cbb93925f52600360205260405f2055611405565b9a01906108cd565b846002949392941c1015610cd95790915f610ca0565b60405162461bcd60e51b815260206004820152601860248201527f3c32352520646966666963756c747920726574617267657400000000000000006044820152606490fd5b60ff600494929394541615610d3c575b5050600191610cbb91611405565b5f52600360205260405f205403610d54578f80610d2e565b60405162461bcd60e51b815260206004820152601560248201527477726f6e6720646966666963756c7479206269747360581b6044820152606490fd5b60405162461bcd60e51b815260206004820152601760248201527f626c6f636b20686173682061626f7665207461726765740000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601e60248201527f706172656e7420626c6f636b206e6f7420796574207375626d697474656400006044820152606490fd5b60405162461bcd60e51b815260206004820152600a602482015269189859081c185c995b9d60b21b6044820152606490fd5b60019450610c07565b14159050865f610c00565b946001830180841161035f57610e78908d146115d2565b8b8203610e905750610e8a8582611620565b946108bb565b94610e9c8383146115d2565b6108bb565b60405162461bcd60e51b81526020600482015260156024820152741bdb1908191a59999a58dd5b1d1e481c195c9a5bd9605a1b6044820152606490fd5b60405162461bcd60e51b815260206004820152602860248201527f546865206368616c6c656e67656420626c6f636b206170706561727320746f206044820152671899481d985b1a5960c21b6064820152608490fd5b60405162461bcd60e51b815260206004820152602660248201527f5468652067656e6573697320626c6f636b2063616e6e6f74206265206368616c6044820152651b195b99d95960d21b6064820152608490fd5b60405162461bcd60e51b815260206004820152601e60248201527f4368616c6c656e676572207573652077726f6e67207369676e617475726500006044820152606490fd5b346100d65760403660031901126100d65760043567ffffffffffffffff81116100d65761100c6110036020923690600401611059565b60243591611173565b6040519015158152f35b346100d6575f3660031901126100d6576020600154604051908152f35b346100d65760203660031901126100d6576020906004355f526003825260405f20548152f35b9181601f840112156100d65782359167ffffffffffffffff83116100d657602083818601950101116100d657565b6044359060ff821682036100d657565b600435906001600160a01b03821682036100d657565b156110b457565b60405162461bcd60e51b81526020600482015260136024820152720eee4dedcce40d0cac2c8cae440d8cadccee8d606b1b6044820152606490fd5b90601f8019910116810190811067ffffffffffffffff82111761111157604052565b634e487b7160e01b5f52604160045260245ffd5b909392938483116100d65784116100d6578101920390565b356001600160e01b0319811692919060048210611158575050565b6001600160e01b031960049290920360031b82901b16169150565b91611180605083146110ad565b80156113395760205f6040518486823780858101838152039060025afa1561046b5760205f80516040518381019182528381526111be6040826110ef565b604051918291518091835e8101838152039060025afa1561046b576111e35f516114c0565b826024116100d6576111f860048501356114c0565b5f19830183811161035f57805f52600260205260405f20541561132f575f52600260205260405f2054036112e65782604c116100d6576112456001600160e01b0319604886013516611412565b916107e080820491066112fc575f19810190811161035f575f52600360205260405f205460ff600454161590816112ee575b506112e6575b10156112e057806004116100d657600263ffffffff833560e881901c62ff00ff1660d89190911c63ff00ff001617601081811c831691901b82161716106112e0576044116100d65760246112d29101356114c0565b156112dc57600190565b5f90565b50505f90565b505050505f90565b90508260021c10155f611277565b60ff60045416159081611318575b501561127d57505050505f90565b90505f52600360205260405f20548214155f61130a565b5050505050505f90565b60405162461bcd60e51b815260206004820152602560248201527f5468652067656e6573697320626c6f636b2063616e6e6f742062652076616c6960448201526419185d195960da1b6064820152608490fd5b91926060938192845260406020850152816040850152848401375f828201840152601f01601f1916010190565b156113c057565b60405162461bcd60e51b815260206004820152601e60248201527f6d757374207375626d6974206174206c65617374206f6e6520626c6f636b00006044820152606490fd5b9190820180921161035f57565b8060031a90600219820191821161035f576001600160fd1b038216820361035f57805f1a908060011a9060021a60081b1760081b179060031b1b90565b6001600160a01b031680156114ad575f5160206116d45f395f51905f5280546001600160a01b0319811683179091556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b631e4fbdf760e01b5f525f60045260245ffd5b7eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff7fff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff008260081b169160081c16177dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff7fffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff00008260101b169160101c16177bffffffff00000000ffffffff00000000ffffffff00000000ffffffff7fffffffff00000000ffffffff00000000ffffffff00000000ffffffff000000008260201b169160201c161777ffffffffffffffff0000000000000000ffffffffffffffff80198260401b169160401c16178060801b9060801c1790565b156115d957565b634e487b7160e01b5f52600160045260245ffd5b5f5160206116d45f395f51905f52546001600160a01b0316330361160d57565b63118cdaa760e01b5f523360045260245ffd5b90815f52600360205260405f20548015611694575f1904916107e08102908082046107e0149015171561035f57810390811161035f576001810180911161035f57600181101580611688575b611675906115d2565b81810291818304149015171561035f5790565b506107e081111561166c565b634e487b7160e01b5f52601260045260245ffd5b60ff5f5160206116f45f395f51905f525460401c16156116c457565b631afcd79f60e31b5f5260045ffdfe9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220ad19500be3251bf3136e9ebf09bb003a0916620990d4d9ad7693b159667b6fbf64736f6c634300081e0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.