Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
| Parent Transaction Hash | Block | From | To | Amount | ||
|---|---|---|---|---|---|---|
| 29828020 | 226 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x0842B40d...7Ee89d851 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
mTokenGateway
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// Copyright (c) 2025 Merge Layers Inc.
//
// This source code is licensed under the Business Source License 1.1
// (the "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
// https://github.com/malda-protocol/malda-lending/blob/main/LICENSE-BSL
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This file contains code derived from or inspired by Compound V2,
// originally licensed under the BSD 3-Clause License. See LICENSE-COMPOUND-V2
// for original license terms and attributions.
// SPDX-License-Identifier: BSL-1.1
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
// contracts
import {IRoles} from "src/interfaces/IRoles.sol";
import {ImTokenGateway} from "src/interfaces/ImTokenGateway.sol";
import {ImTokenOperationTypes} from "src/interfaces/ImToken.sol";
import {mTokenProofDecoderLib} from "src/libraries/mTokenProofDecoderLib.sol";
import {IZkVerifier} from "src/verifier/ZkVerifier.sol";
contract mTokenGateway is OwnableUpgradeable, ImTokenGateway, ImTokenOperationTypes {
using SafeERC20 for IERC20;
// ----------- STORAGE -----------
/**
* @inheritdoc ImTokenGateway
*/
IRoles public rolesOperator;
IZkVerifier public verifier;
mapping(OperationType => bool) public paused;
/**
* @inheritdoc ImTokenGateway
*/
address public underlying;
mapping(address => uint256) public accAmountIn;
mapping(address => uint256) public accAmountOut;
mapping(address => mapping(address => bool)) public allowedCallers;
mapping(address => bool) public userWhitelisted;
bool public whitelistEnabled;
uint32 private constant LINEA_CHAIN_ID = 59141; // TESTNET; TODO: change back to 59144
///@dev gas fee for `supplyOnHost`
uint256 public gasFee;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(address payable _owner, address _underlying, address _roles, address zkVerifier_)
external
initializer
{
__Ownable_init(_owner);
require(zkVerifier_ != address(0), mTokenGateway_AddressNotValid());
underlying = _underlying;
rolesOperator = IRoles(_roles);
verifier = IZkVerifier(zkVerifier_);
}
modifier notPaused(OperationType _type) {
require(!paused[_type], mTokenGateway_Paused(_type));
_;
}
modifier onlyAllowedUser(address user) {
if (whitelistEnabled) {
require(userWhitelisted[user], mTokenGateway_UserNotWhitelisted());
}
_;
}
// ----------- VIEW ------------
/**
* @inheritdoc ImTokenGateway
*/
function isPaused(OperationType _type) external view returns (bool) {
return paused[_type];
}
/**
* @inheritdoc ImTokenGateway
*/
function getProofData(address user, uint32) external view returns (uint256, uint256) {
return (accAmountIn[user], accAmountOut[user]);
}
// ----------- OWNER ------------
/**
* @notice Sets user whitelist status
* @param user The user address
* @param state The new staate
*/
function setWhitelistedUser(address user, bool state) external onlyOwner {
userWhitelisted[user] = state;
emit mTokenGateway_UserWhitelisted(user, state);
}
/**
* @notice Enable user whitelist
*/
function enableWhitelist() external onlyOwner {
whitelistEnabled = true;
emit mTokenGateway_WhitelistEnabled();
}
/**
* @notice Disable user whitelist
*/
function disableWhitelist() external onlyOwner {
whitelistEnabled = false;
emit mTokenGateway_WhitelistDisabled();
}
/**
* @inheritdoc ImTokenGateway
*/
function setPaused(OperationType _type, bool state) external override {
if (state) {
require(
msg.sender == owner() || rolesOperator.isAllowedFor(msg.sender, rolesOperator.GUARDIAN_PAUSE()),
mTokenGateway_CallerNotAllowed()
);
} else {
require(msg.sender == owner(), mTokenGateway_CallerNotAllowed());
}
emit mTokenGateway_PausedState(_type, state);
paused[_type] = state;
}
/**
* @inheritdoc ImTokenGateway
*/
function extractForRebalancing(uint256 amount) external notPaused(OperationType.Rebalancing) {
if (!rolesOperator.isAllowedFor(msg.sender, rolesOperator.REBALANCER())) revert mTokenGateway_NotRebalancer();
IERC20(underlying).safeTransfer(msg.sender, amount);
}
// temporary. V1.0.1
function setUnderlying(address _addr) external onlyOwner {
underlying = _addr;
}
/**
* @notice Sets the gas fee
* @param amount the new gas fee
*/
function setGasFee(uint256 amount) external onlyOwner {
gasFee = amount;
emit mTokenGateway_GasFeeUpdated(amount);
}
/**
* @notice Withdraw gas received so far
* @param receiver the receiver address
*/
function withdrawGasFees(address payable receiver) external {
if (msg.sender != owner() && !_isAllowedFor(msg.sender, _getSequencerRole())) {
revert mTokenGateway_CallerNotAllowed();
}
uint256 balance = address(this).balance;
receiver.transfer(balance);
}
/**
* @notice Updates IZkVerifier address
* @param _zkVerifier the verifier address
*/
function updateZkVerifier(address _zkVerifier) external onlyOwner {
require(_zkVerifier != address(0), mTokenGateway_AddressNotValid());
emit ZkVerifierUpdated(address(verifier), _zkVerifier);
verifier = IZkVerifier(_zkVerifier);
}
// ----------- PUBLIC ------------
/**
* @inheritdoc ImTokenGateway
*/
function updateAllowedCallerStatus(address caller, bool status) external override {
allowedCallers[msg.sender][caller] = status;
emit AllowedCallerUpdated(msg.sender, caller, status);
}
/**
* @inheritdoc ImTokenGateway
*/
function supplyOnHost(uint256 amount, address receiver, bytes4 lineaSelector)
external
payable
override
notPaused(OperationType.AmountIn)
onlyAllowedUser(msg.sender)
{
// checks
require(amount > 0, mTokenGateway_AmountNotValid());
require(msg.value >= gasFee, mTokenGateway_NotEnoughGasFee());
IERC20(underlying).safeTransferFrom(msg.sender, address(this), amount);
// effects
accAmountIn[receiver] += amount;
emit mTokenGateway_Supplied(
msg.sender,
receiver,
accAmountIn[receiver],
accAmountOut[receiver],
amount,
uint32(block.chainid),
LINEA_CHAIN_ID,
lineaSelector
);
}
/**
* @inheritdoc ImTokenGateway
*/
function outHere(bytes calldata journalData, bytes calldata seal, uint256[] calldata amounts, address receiver)
external
notPaused(OperationType.AmountOutHere)
{
// verify received data
if (!rolesOperator.isAllowedFor(msg.sender, rolesOperator.PROOF_BATCH_FORWARDER())) {
_verifyProof(journalData, seal);
}
bytes[] memory journals = abi.decode(journalData, (bytes[]));
uint256 length = journals.length;
require(length == amounts.length, mTokenGateway_LengthNotValid());
for (uint256 i; i < journals.length;) {
_outHere(journals[i], amounts[i], receiver);
unchecked {
++i;
}
}
}
function _outHere(bytes memory journalData, uint256 amount, address receiver) internal {
(address _sender, address _market,, uint256 _accAmountOut, uint32 _chainId, uint32 _dstChainId,) =
mTokenProofDecoderLib.decodeJournal(journalData);
// temporary overwrite; will be removed in future implementations
receiver = _sender;
// checks
_checkSender(msg.sender, _sender);
require(_market == address(this), mTokenGateway_AddressNotValid());
require(_chainId == LINEA_CHAIN_ID, mTokenGateway_ChainNotValid()); // allow only Host
require(_dstChainId == uint32(block.chainid), mTokenGateway_ChainNotValid());
require(amount > 0, mTokenGateway_AmountNotValid());
require(_accAmountOut - accAmountOut[_sender] >= amount, mTokenGateway_AmountTooBig());
require(IERC20(underlying).balanceOf(address(this)) >= amount, mTokenGateway_ReleaseCashNotAvailable());
// effects
accAmountOut[_sender] += amount;
// interactions
IERC20(underlying).safeTransfer(_sender, amount);
emit mTokenGateway_Extracted(
msg.sender,
_sender,
receiver,
accAmountIn[_sender],
accAmountOut[_sender],
amount,
uint32(_chainId),
uint32(block.chainid)
);
}
// ----------- PRIVATE ------------
function _verifyProof(bytes calldata journalData, bytes calldata seal) private view {
require(journalData.length > 0, mTokenGateway_JournalNotValid());
// Decode the dynamic array of journals.
bytes[] memory journals = abi.decode(journalData, (bytes[]));
// Check the L1Inclusion flag for each journal.
bool isSequencer = _isAllowedFor(msg.sender, _getProofForwarderRole()) ||
_isAllowedFor(msg.sender, _getBatchProofForwarderRole());
if (!isSequencer) {
for (uint256 i = 0; i < journals.length; i++) {
(, , , , , , bool L1Inclusion) = mTokenProofDecoderLib.decodeJournal(journals[i]);
if (!L1Inclusion) {
revert mTokenGateway_L1InclusionRequired();
}
}
}
// verify it using the ZkVerifier contract
verifier.verifyInput(journalData, seal);
}
function _checkSender(address msgSender, address srcSender) private view {
if (msgSender != srcSender) {
require(
allowedCallers[srcSender][msgSender] || msgSender == owner()
|| _isAllowedFor(msgSender, _getProofForwarderRole())
|| _isAllowedFor(msgSender, _getBatchProofForwarderRole()),
mTokenGateway_CallerNotAllowed()
);
}
}
function _getSequencerRole() private view returns (bytes32) {
return rolesOperator.SEQUENCER();
}
function _getBatchProofForwarderRole() private view returns (bytes32) {
return rolesOperator.PROOF_BATCH_FORWARDER();
}
function _getProofForwarderRole() private view returns (bytes32) {
return rolesOperator.PROOF_FORWARDER();
}
function _isAllowedFor(address _sender, bytes32 role) private view returns (bool) {
return rolesOperator.isAllowedFor(_sender, role);
}
}// 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// 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);
}
}// Copyright (c) 2025 Merge Layers Inc.
//
// This source code is licensed under the Business Source License 1.1
// (the "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
// https://github.com/malda-protocol/malda-lending/blob/main/LICENSE-BSL
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This file contains code derived from or inspired by Compound V2,
// originally licensed under the BSD 3-Clause License. See LICENSE-COMPOUND-V2
// for original license terms and attributions.
// SPDX-License-Identifier: BSL-1.1
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
interface IRoles {
error Roles_InputNotValid();
/**
* @notice Returns REBALANCER role
*/
function REBALANCER() external view returns (bytes32);
/**
* @notice Returns REBALANCER_EOA role
*/
function REBALANCER_EOA() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_PAUSE role
*/
function GUARDIAN_PAUSE() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_BRIDGE role
*/
function GUARDIAN_BRIDGE() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_BORROW_CAP role
*/
function GUARDIAN_BORROW_CAP() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_SUPPLY_CAP role
*/
function GUARDIAN_SUPPLY_CAP() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_RESERVE role
*/
function GUARDIAN_RESERVE() external view returns (bytes32);
/**
* @notice Returns PROOF_FORWARDER role
*/
function PROOF_FORWARDER() external view returns (bytes32);
/**
* @notice Returns PROOF_BATCH_FORWARDER role
*/
function PROOF_BATCH_FORWARDER() external view returns (bytes32);
/**
* @notice Returns SEQUENCER role
*/
function SEQUENCER() external view returns (bytes32);
/**
* @notice Returns PAUSE_MANAGER role
*/
function PAUSE_MANAGER() external view returns (bytes32);
/**
* @notice Returns CHAINS_MANAGER role
*/
function CHAINS_MANAGER() external view returns (bytes32);
/**
* @notice Returns GUARDIAN_ORACLE role
*/
function GUARDIAN_ORACLE() external view returns (bytes32);
/**
* @notice Returns allowance status for a contract and a role
* @param _contract the contract address
* @param _role the bytes32 role
*/
function isAllowedFor(address _contract, bytes32 _role) external view returns (bool);
}// Copyright (c) 2025 Merge Layers Inc.
//
// This source code is licensed under the Business Source License 1.1
// (the "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
// https://github.com/malda-protocol/malda-lending/blob/main/LICENSE-BSL
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This file contains code derived from or inspired by Compound V2,
// originally licensed under the BSD 3-Clause License. See LICENSE-COMPOUND-V2
// for original license terms and attributions.
// SPDX-License-Identifier: BSL-1.1
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
import {IRoles} from "./IRoles.sol";
import {ImTokenOperationTypes} from "./ImToken.sol";
interface ImTokenGateway {
// ----------- EVENTS -----------
/**
* @notice Emitted when a user updates allowed callers
*/
event AllowedCallerUpdated(address indexed sender, address indexed caller, bool status);
/**
* @notice Emitted when a supply operation is initiated
*/
event mTokenGateway_Supplied(
address indexed from,
address indexed receiver,
uint256 accAmountIn,
uint256 accAmountOut,
uint256 amount,
uint32 srcChainId,
uint32 dstChainId,
bytes4 lineaMethodSelector
);
/**
* @notice Emitted when an extract was finalized
*/
event mTokenGateway_Extracted(
address indexed msgSender,
address indexed srcSender,
address indexed receiver,
uint256 accAmountIn,
uint256 accAmountOut,
uint256 amount,
uint32 srcChainId,
uint32 dstChainId
);
/**
* @notice Emitted when a proof was skipped
*/
event mTokenGateway_Skipped(
address indexed msgSender,
address indexed srcSender,
address indexed receiver,
uint256 accAmountIn,
uint256 accAmountOut,
uint256 amount,
uint32 srcChainId,
uint32 dstChainId
);
/**
* @notice Emitted when the gas fee is updated
*/
event mTokenGateway_GasFeeUpdated(uint256 amount);
event mTokenGateway_PausedState(ImTokenOperationTypes.OperationType indexed _type, bool _status);
event ZkVerifierUpdated(address indexed oldVerifier, address indexed newVerifier);
event mTokenGateway_UserWhitelisted(address indexed user, bool status);
event mTokenGateway_WhitelistEnabled();
event mTokenGateway_WhitelistDisabled();
// ----------- ERRORS -----------+
/**
* @notice Thrown when the chain id is not LINEA
*/
error mTokenGateway_ChainNotValid();
/**
* @notice Thrown when the address is not valid
*/
error mTokenGateway_AddressNotValid();
/**
* @notice Thrown when the amount specified is invalid (e.g., zero)
*/
error mTokenGateway_AmountNotValid();
/**
* @notice Thrown when the journal data provided is invalid
*/
error mTokenGateway_JournalNotValid();
/**
* @notice Thrown when there is insufficient cash to release the specified amount
*/
error mTokenGateway_AmountTooBig();
/**
* @notice Thrown when there is insufficient cash to release the specified amount
*/
error mTokenGateway_ReleaseCashNotAvailable();
/**
* @notice Thrown when token is tranferred
*/
error mTokenGateway_NonTransferable();
/**
* @notice Thrown when caller is not allowed
*/
error mTokenGateway_CallerNotAllowed();
/**
* @notice Thrown when market is paused for operation type
*/
error mTokenGateway_Paused(ImTokenOperationTypes.OperationType _type);
/**
* @notice Thrown when caller is not rebalancer
*/
error mTokenGateway_NotRebalancer();
/**
* @notice Thrown when length is not valid
*/
error mTokenGateway_LengthNotValid();
/**
* @notice Thrown when not enough gas fee was received
*/
error mTokenGateway_NotEnoughGasFee();
/**
* @notice Thrown when L1 inclusion is required
*/
error mTokenGateway_L1InclusionRequired();
/**
* @notice Thrown when user is not whitelisted
*/
error mTokenGateway_UserNotWhitelisted();
// ----------- VIEW -----------
/**
* @notice Roles manager
*/
function rolesOperator() external view returns (IRoles);
/**
* @notice Returns the address of the underlying token
* @return The address of the underlying token
*/
function underlying() external view returns (address);
/**
* @notice returns pause state for operation
* @param _type the operation type
*/
function isPaused(ImTokenOperationTypes.OperationType _type) external view returns (bool);
/**
* @notice Returns accumulated amount in per user
*/
function accAmountIn(address user) external view returns (uint256);
/**
* @notice Returns accumulated amount out per user
*/
function accAmountOut(address user) external view returns (uint256);
/**
* @notice Returns the proof data journal
*/
function getProofData(address user, uint32 dstId) external view returns (uint256, uint256);
// ----------- PUBLIC -----------
/**
* @notice Extract amount to be used for rebalancing operation
* @param amount The amount to rebalance
*/
function extractForRebalancing(uint256 amount) external;
/**
* @notice Set pause for a specific operation
* @param _type The pause operation type
* @param state The pause operation status
*/
function setPaused(ImTokenOperationTypes.OperationType _type, bool state) external;
/**
* @notice Set caller status for `msg.sender`
* @param caller The caller address
* @param status The status to set for `caller`
*/
function updateAllowedCallerStatus(address caller, bool status) external;
/**
* @notice Supply underlying to the contract
* @param amount The supplied amount
* @param receiver The receiver address
* @param lineaSelector The method selector to be called on Linea by our relayer. If empty, user has to submit it
*/
function supplyOnHost(uint256 amount, address receiver, bytes4 lineaSelector) external payable;
/**
* @notice Extract tokens
* @param journalData The supplied journal
* @param seal The seal address
* @param amounts The amounts to withdraw for each journal
* @param receiver The receiver address
*/
function outHere(bytes calldata journalData, bytes calldata seal, uint256[] memory amounts, address receiver)
external;
}// Copyright (c) 2025 Merge Layers Inc.
//
// This source code is licensed under the Business Source License 1.1
// (the "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
// https://github.com/malda-protocol/malda-lending/blob/main/LICENSE-BSL
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This file contains code derived from or inspired by Compound V2,
// originally licensed under the BSD 3-Clause License. See LICENSE-COMPOUND-V2
// for original license terms and attributions.
// SPDX-License-Identifier: BSL-1.1
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
import {IRoles} from "./IRoles.sol";
interface ImTokenOperationTypes {
enum OperationType {
AmountIn,
AmountInHere,
AmountOut,
AmountOutHere,
Seize,
Transfer,
Mint,
Borrow,
Repay,
Redeem,
Liquidate,
Rebalancing
}
}
interface ImTokenDelegator {
/**
* @notice Non-standard token able to delegate
*/
function delegate(address delegatee) external;
}
interface ImTokenMinimal {
/**
* @notice EIP-20 token name for this token
*/
function name() external view returns (string memory);
/**
* @notice EIP-20 token symbol for this token
*/
function symbol() external view returns (string memory);
/**
* @notice EIP-20 token decimals for this token
*/
function decimals() external view returns (uint8);
/**
* @notice Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @notice Returns the amount of underlying tokens
*/
function totalUnderlying() external view returns (uint256);
/**
* @notice Returns the value of tokens owned by `account`.
* @param account The account to check for
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Returns the underlying address
*/
function underlying() external view returns (address);
}
interface ImToken is ImTokenMinimal {
// ----------- STORAGE ------------
/**
* @notice Roles manager
*/
function rolesOperator() external view returns (IRoles);
/**
* @notice Administrator for this contract
*/
function admin() external view returns (address payable);
/**
* @notice Pending administrator for this contract
*/
function pendingAdmin() external view returns (address payable);
/**
* @notice Contract which oversees inter-mToken operations
*/
function operator() external view returns (address);
/**
* @notice Model which tells what the current interest rate should be
*/
function interestRateModel() external view returns (address);
/**
* @notice Fraction of interest currently set aside for reserves
*/
function reserveFactorMantissa() external view returns (uint256);
/**
* @notice Block timestamp that interest was last accrued at
*/
function accrualBlockTimestamp() external view returns (uint256);
/**
* @notice Accumulator of the total earned interest rate since the opening of the market
*/
function borrowIndex() external view returns (uint256);
/**
* @notice Total amount of outstanding borrows of the underlying in this market
*/
function totalBorrows() external view returns (uint256);
/**
* @notice Total amount of reserves of the underlying held in this market
*/
function totalReserves() external view returns (uint256);
// ----------- ACTIONS ------------
/**
* @notice Transfers `amount` tokens to the `dst` address
* @param dst The address of the recipient
* @param amount The number of tokens to transfer
* @return Whether the transfer was successful or not
*/
function transfer(address dst, uint256 amount) external returns (bool);
/**
* @notice Transfers `amount` tokens from the `src` address to the `dst` address
* @param src The address from which tokens are transferred
* @param dst The address to which tokens are transferred
* @param amount The number of tokens to transfer
* @return Whether the transfer was successful or not
*/
function transferFrom(address src, address dst, uint256 amount) external returns (bool);
/**
* @notice Approves `spender` to spend `amount` tokens on behalf of the caller
* @param spender The address authorized to spend tokens
* @param amount The number of tokens to approve
* @return Whether the approval was successful or not
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @notice Returns the current allowance the `spender` has from the `owner`
* @param owner The address of the token holder
* @param spender The address authorized to spend the tokens
* @return The current remaining number of tokens `spender` can spend
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @notice Returns the balance of tokens held by `owner`
* @param owner The address to query the balance for
* @return The balance of tokens owned by `owner`
*/
function balanceOf(address owner) external view returns (uint256);
/**
* @notice Returns the underlying asset balance of the `owner`
* @param owner The address to query the balance of underlying assets for
* @return The balance of underlying assets owned by `owner`
*/
function balanceOfUnderlying(address owner) external returns (uint256);
/**
* @notice Returns the snapshot of account details for the given `account`
* @param account The address to query the account snapshot for
* @return (token balance, borrow balance, exchange rate)
*/
function getAccountSnapshot(address account) external view returns (uint256, uint256, uint256);
/**
* @notice Returns the current borrow rate per block
* @return The current borrow rate per block, scaled by 1e18
*/
function borrowRatePerBlock() external view returns (uint256);
/**
* @notice Returns the current supply rate per block
* @return The current supply rate per block, scaled by 1e18
*/
function supplyRatePerBlock() external view returns (uint256);
/**
* @notice Returns the total amount of borrows, accounting for interest
* @return The total amount of borrows
*/
function totalBorrowsCurrent() external returns (uint256);
/**
* @notice Returns the current borrow balance for `account`, accounting for interest
* @param account The address to query the borrow balance for
* @return The current borrow balance
*/
function borrowBalanceCurrent(address account) external returns (uint256);
/**
* @notice Returns the stored borrow balance for `account`, without accruing interest
* @param account The address to query the stored borrow balance for
* @return The stored borrow balance
*/
function borrowBalanceStored(address account) external view returns (uint256);
/**
* @notice Returns the current exchange rate, with interest accrued
* @return The current exchange rate
*/
function exchangeRateCurrent() external returns (uint256);
/**
* @notice Returns the stored exchange rate, without accruing interest
* @return The stored exchange rate
*/
function exchangeRateStored() external view returns (uint256);
/**
* @notice Returns the total amount of available cash in the contract
* @return The total amount of cash
*/
function getCash() external view returns (uint256);
/**
* @notice Accrues interest on the contract's outstanding loans
*/
function accrueInterest() external;
/**
* @notice Transfers collateral tokens (this market) to the liquidator.
* @dev Will fail unless called by another mToken during the process of liquidation.
* Its absolutely critical to use msg.sender as the borrowed mToken and not a parameter.
* @param liquidator The account receiving seized collateral
* @param borrower The account having collateral seized
* @param seizeTokens The number of mTokens to seize
*/
function seize(address liquidator, address borrower, uint256 seizeTokens) external;
/**
* @notice Accrues interest and reduces reserves by transferring to admin
* @param reduceAmount Amount of reduction to reserves
*/
function reduceReserves(uint256 reduceAmount) external;
}// Copyright (c) 2025 Merge Layers Inc.
//
// This source code is licensed under the Business Source License 1.1
// (the "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
// https://github.com/malda-protocol/malda-lending/blob/main/LICENSE-BSL
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This file contains code derived from or inspired by Compound V2,
// originally licensed under the BSD 3-Clause License. See LICENSE-COMPOUND-V2
// for original license terms and attributions.
// SPDX-License-Identifier: BSL-1.1
pragma solidity =0.8.28;
import {BytesLib} from "src/libraries/BytesLib.sol";
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
library mTokenProofDecoderLib {
uint256 public constant ENTRY_SIZE = 113; // 112 + 1 for L1inclusion
error mTokenProofDecoderLib_ChainNotFound();
error mTokenProofDecoderLib_InvalidLength();
error mTokenProofDecoderLib_InvalidInclusion();
function decodeJournal(bytes memory journalData)
internal
pure
returns (
address sender,
address market,
uint256 accAmountIn,
uint256 accAmountOut,
uint32 chainId,
uint32 dstChainId,
bool L1inclusion
)
{
require (journalData.length == ENTRY_SIZE, mTokenProofDecoderLib_InvalidLength());
// decode action data
// | Offset | Length | Data Type |
// |--------|---------|----------------------- |
// | 0 | 20 | address sender |
// | 20 | 20 | address market |
// | 40 | 32 | uint256 accAmountIn |
// | 72 | 32 | uint256 accAmountOut |
// | 104 | 4 | uint32 chainId |
// | 108 | 4 | uint32 dstChainId |
// | 112 | 1 | bool L1inclusion |
sender = BytesLib.toAddress(BytesLib.slice(journalData, 0, 20), 0);
market = BytesLib.toAddress(BytesLib.slice(journalData, 20, 20), 0);
accAmountIn = BytesLib.toUint256(BytesLib.slice(journalData, 40, 32), 0);
accAmountOut = BytesLib.toUint256(BytesLib.slice(journalData, 72, 32), 0);
chainId = BytesLib.toUint32(BytesLib.slice(journalData, 104, 4), 0);
dstChainId = BytesLib.toUint32(BytesLib.slice(journalData, 108, 4), 0);
uint8 rawL1inclusion = BytesLib.toUint8(BytesLib.slice(journalData, 112, 1), 0);
require(rawL1inclusion == 0 || rawL1inclusion == 1, mTokenProofDecoderLib_InvalidInclusion());
L1inclusion = rawL1inclusion == 1;
}
function encodeJournal(
address sender,
address market,
uint256 accAmountIn,
uint256 accAmountOut,
uint32 chainId,
uint32 dstChainId,
bool L1inclusion
) internal pure returns (bytes memory) {
return abi.encodePacked(sender, market, accAmountIn, accAmountOut, chainId, dstChainId, L1inclusion);
}
}// Copyright (c) 2025 Merge Layers Inc.
//
// This source code is licensed under the Business Source License 1.1
// (the "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
// https://github.com/malda-protocol/malda-lending/blob/main/LICENSE-BSL
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This file contains code derived from or inspired by Risc0,
// originally licensed under the Apache License 2.0. See LICENSE-RISC0
// and the NOTICE file for original license terms and attributions.
// SPDX-License-Identifier: AGPL-3.0
pragma solidity =0.8.28;
/*
_____ _____ __ ____ _____
| | _ | | | \| _ |
| | | | | |__| | | |
|_|_|_|__|__|_____|____/|__|__|
*/
// interfaces
import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol";
// contracts
import {Steel} from "risc0/steel/Steel.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
interface IZkVerifier {
function verifyInput(bytes calldata journalEntry, bytes calldata seal) external view;
}
contract ZkVerifier is Ownable, IZkVerifier {
// ----------- STORAGE ------------
IRiscZeroVerifier public verifier;
bytes32 public imageId;
error ZkVerifier_ImageNotValid();
error ZkVerifier_InputNotValid();
error ZkVerifier_VerifierNotSet();
event ImageSet(bytes32 _imageId);
event VerifierSet(address indexed oldVerifier, address indexed newVerifier);
constructor(address _owner, bytes32 _imageId, address _verifier) Ownable(_owner) {
require(_verifier != address(0), ZkVerifier_InputNotValid());
verifier = IRiscZeroVerifier(_verifier);
imageId = _imageId;
}
// ----------- OWNER ------------
/**
* @notice Sets the _risc0Verifier address
* @dev Admin check is needed on the external method
* @param _risc0Verifier the new IRiscZeroVerifier address
*/
function setVerifier(address _risc0Verifier) external onlyOwner {
require(_risc0Verifier != address(0), ZkVerifier_InputNotValid());
emit VerifierSet(address(verifier), _risc0Verifier);
verifier = IRiscZeroVerifier(_risc0Verifier);
}
/**
* @notice Sets the image id
* @dev Admin check is needed on the external method
* @param _imageId the new image id
*/
function setImageId(bytes32 _imageId) external onlyOwner {
require(_imageId != bytes32(0), ZkVerifier_ImageNotValid());
emit ImageSet(_imageId);
imageId = _imageId;
}
// ----------- VIEW ------------
/**
* @notice Verifies an input
* @param journalEntry the risc0 journal entry
* @param seal the risc0 seal
*/
function verifyInput(bytes calldata journalEntry, bytes calldata seal) external view {
// generic checks
_checkAddresses();
// verify input
__verify(journalEntry, seal);
}
// ----------- PRIVATE ------------
function _checkAddresses() private view {
require(address(verifier) != address(0), ZkVerifier_VerifierNotSet());
}
function __verify(bytes calldata journalEntry, bytes calldata seal) private view {
verifier.verify(seal, imageId, sha256(journalEntry));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// 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.0.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 reininitialization) 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 Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: Unlicense /* * @title Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> * * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ pragma solidity =0.8.28; library BytesLib { function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore( 0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. ) ) } return tempBytes; } function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot // because arrays use the entire slot.) let fslot := sload(_preBytes.slot) // Arrays of 31 bytes or less have an even value in their slot, // while longer arrays have an odd value. The actual length is // the slot divided by two for odd values, and the lowest order // byte divided by two for even values. // If the slot is even, bitwise and the slot with 255 and divide by // two to get the length. If the slot is odd, bitwise and the slot // with -1 and divide by two. let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) let newlength := add(slength, mlength) // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage switch add(lt(slength, 32), lt(newlength, 32)) case 2 { // Since the new array still fits in the slot, we just need to // update the contents of the slot. // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length sstore( _preBytes.slot, // all the modifications to the slot are inside this // next block add( // we can just add to the slot contents because the // bytes we want to change are the LSBs fslot, add( mul( div( // load the bytes from memory mload(add(_postBytes, 0x20)), // zero all bytes to the right exp(0x100, sub(32, mlength)) ), // and now shift left the number of bytes to // leave space for the length in the slot exp(0x100, sub(32, newlength)) ), // increase length by the double of the memory // bytes length mul(mlength, 2) ) ) ) } case 1 { // The stored value fits in the slot, but the combined value // will exceed it. // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // The contents of the _postBytes array start 32 bytes into // the structure. Our first read should obtain the `submod` // bytes that can fit into the unused space in the last word // of the stored array. To get this, we read 32 bytes starting // from `submod`, so the data we read overlaps with the array // contents by `submod` bytes. Masking the lowest-order // `submod` bytes allows us to add that value directly to the // stored value. let submod := sub(32, slength) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore( sc, add( and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00), and(mload(mc), mask) ) ) for { mc := add(mc, 0x20) sc := add(sc, 1) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } default { // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) // Start copying to the last used word of the stored array. let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // Copy over the first `submod` bytes of the new data as in // case 1 above. let slengthmod := mod(slength, 32) let mlengthmod := mod(mlength, 32) let submod := sub(32, slengthmod) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore(sc, add(sload(sc), and(mload(mc), mask))) for { sc := add(sc, 1) mc := add(mc, 0x20) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } } } function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { require(_length + 31 >= _length, "slice_overflow"); require(_bytes.length >= _start + _length, "slice_outOfBounds"); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { require(_bytes.length >= _start + 1, "toUint8_outOfBounds"); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { require(_bytes.length >= _start + 4, "toUint32_outOfBounds"); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { require(_bytes.length >= _start + 8, "toUint64_outOfBounds"); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { require(_bytes.length >= _start + 12, "toUint96_outOfBounds"); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { require(_bytes.length >= _start + 16, "toUint128_outOfBounds"); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { require(_bytes.length >= _start + 32, "toBytes32_outOfBounds"); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let mc := add(_preBytes, 0x20) let end := add(mc, length) for { let cc := add(_postBytes, 0x20) } // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } } default { // unsuccess: success := 0 } } return success; } function equal_nonAligned(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let endMinusWord := add(_preBytes, length) let mc := add(_preBytes, 0x20) let cc := add(_postBytes, 0x20) for { // the next line is the loop condition: // while(uint256(mc < endWord) + cb == 2) } eq(add(lt(mc, endMinusWord), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } // Only if still successful // For <1 word tail bytes if gt(success, 0) { // Get the remainder of length/32 // length % 32 = AND(length, 32 - 1) let numTailBytes := and(length, 0x1f) let mcRem := mload(mc) let ccRem := mload(cc) for { let i := 0 } // the next line is the loop condition: // while(uint256(i < numTailBytes) + cb == 2) eq(add(lt(i, numTailBytes), cb), 2) { i := add(i, 1) } { if iszero(eq(byte(i, mcRem), byte(i, ccRem))) { // unsuccess: success := 0 cb := 0 } } } } default { // unsuccess: success := 0 } } return success; } function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) { bool success = true; assembly { // we know _preBytes_offset is 0 let fslot := sload(_preBytes.slot) // Decode the length of the stored array like in concatStorage(). let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) // if lengths don't match the arrays are not equal switch eq(slength, mlength) case 1 { // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage if iszero(iszero(slength)) { switch lt(slength, 32) case 1 { // blank the last byte which is the length fslot := mul(div(fslot, 0x100), 0x100) if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { // unsuccess: success := 0 } } default { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := keccak256(0x0, 0x20) let mc := add(_postBytes, 0x20) let end := add(mc, mlength) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) for {} eq(add(lt(mc, end), cb), 2) { sc := add(sc, 1) mc := add(mc, 0x20) } { if iszero(eq(sload(sc), mload(mc))) { // unsuccess: success := 0 cb := 0 } } } } } default { // unsuccess: success := 0 } } return success; } }
// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.9;
import {reverseByteOrderUint32} from "./Util.sol";
/// @notice A receipt attesting to the execution of a guest program.
/// @dev A receipt contains two parts: a seal and a claim. The seal is a zero-knowledge proof
/// attesting to knowledge of a zkVM execution resulting in the claim. The claim is a set of public
/// outputs for the execution. Crucially, the claim includes the journal and the image ID. The
/// image ID identifies the program that was executed, and the journal is the public data written
/// by the program. Note that this struct only contains the claim digest, as can be obtained with
/// the `digest()` function on `ReceiptClaimLib`.
struct Receipt {
bytes seal;
bytes32 claimDigest;
}
/// @notice Public claims about a zkVM guest execution, such as the journal committed to by the guest.
/// @dev Also includes important information such as the exit code and the starting and ending system
/// state (i.e. the state of memory). `ReceiptClaim` is a "Merkle-ized struct" supporting
/// partial openings of the underlying fields from a hash commitment to the full structure.
struct ReceiptClaim {
/// @notice Digest of the SystemState just before execution has begun.
bytes32 preStateDigest;
/// @notice Digest of the SystemState just after execution has completed.
bytes32 postStateDigest;
/// @notice The exit code for the execution.
ExitCode exitCode;
/// @notice A digest of the input to the guest.
/// @dev This field is currently unused and must be set to the zero digest.
bytes32 input;
/// @notice Digest of the Output of the guest, including the journal
/// and assumptions set during execution.
bytes32 output;
}
library ReceiptClaimLib {
using OutputLib for Output;
using SystemStateLib for SystemState;
bytes32 constant TAG_DIGEST = sha256("risc0.ReceiptClaim");
// Define a constant to ensure hashing is done at compile time. Can't use the
// SystemStateLib.digest method here because the Solidity compiler complains.
bytes32 constant SYSTEM_STATE_ZERO_DIGEST = 0xa3acc27117418996340b84e5a90f3ef4c49d22c79e44aad822ec9c313e1eb8e2;
/// @notice Construct a ReceiptClaim from the given imageId and journalDigest.
/// Returned ReceiptClaim will represent a successful execution of the zkVM, running
/// the program committed by imageId and resulting in the journal specified by
/// journalDigest.
/// @param imageId The identifier for the guest program.
/// @param journalDigest The SHA-256 digest of the journal bytes.
/// @dev Input hash and postStateDigest are set to all-zeros (i.e. no committed input, or
/// final memory state), the exit code is (Halted, 0), and there are no assumptions
/// (i.e. the receipt is unconditional).
function ok(bytes32 imageId, bytes32 journalDigest) internal pure returns (ReceiptClaim memory) {
return ReceiptClaim(
imageId,
SYSTEM_STATE_ZERO_DIGEST,
ExitCode(SystemExitCode.Halted, 0),
bytes32(0),
Output(journalDigest, bytes32(0)).digest()
);
}
function digest(ReceiptClaim memory claim) internal pure returns (bytes32) {
return sha256(
abi.encodePacked(
TAG_DIGEST,
// down
claim.input,
claim.preStateDigest,
claim.postStateDigest,
claim.output,
// data
uint32(claim.exitCode.system) << 24,
uint32(claim.exitCode.user) << 24,
// down.length
uint16(4) << 8
)
);
}
}
/// @notice Commitment to the memory state and program counter (pc) of the zkVM.
/// @dev The "pre" and "post" fields of the ReceiptClaim are digests of the system state at the
/// start are stop of execution. Programs are loaded into the zkVM by creating a memory image
/// of the loaded program, and creating a system state for initializing the zkVM. This is
/// known as the "image ID".
struct SystemState {
/// @notice Program counter.
uint32 pc;
/// @notice Root hash of a merkle tree which confirms the integrity of the memory image.
bytes32 merkle_root;
}
library SystemStateLib {
bytes32 constant TAG_DIGEST = sha256("risc0.SystemState");
function digest(SystemState memory state) internal pure returns (bytes32) {
return sha256(
abi.encodePacked(
TAG_DIGEST,
// down
state.merkle_root,
// data
reverseByteOrderUint32(state.pc),
// down.length
uint16(1) << 8
)
);
}
}
/// @notice Exit condition indicated by the zkVM at the end of the guest execution.
/// @dev Exit codes have a "system" part and a "user" part. Semantically, the system part is set to
/// indicate the type of exit (e.g. halt, pause, or system split) and is directly controlled by the
/// zkVM. The user part is an exit code, similar to exit codes used in Linux, chosen by the guest
/// program to indicate additional information (e.g. 0 to indicate success or 1 to indicate an
/// error).
struct ExitCode {
SystemExitCode system;
uint8 user;
}
/// @notice Exit condition indicated by the zkVM at the end of the execution covered by this proof.
/// @dev
/// `Halted` indicates normal termination of a program with an interior exit code returned from the
/// guest program. A halted program cannot be resumed.
///
/// `Paused` indicates the execution ended in a paused state with an interior exit code set by the
/// guest program. A paused program can be resumed such that execution picks up where it left
/// of, with the same memory state.
///
/// `SystemSplit` indicates the execution ended on a host-initiated system split. System split is
/// mechanism by which the host can temporarily stop execution of the execution ended in a system
/// split has no output and no conclusions can be drawn about whether the program will eventually
/// halt. System split is used in continuations to split execution into individually provable segments.
enum SystemExitCode {
Halted,
Paused,
SystemSplit
}
/// @notice Output field in the `ReceiptClaim`, committing to a claimed journal and assumptions list.
struct Output {
/// @notice Digest of the journal committed to by the guest execution.
bytes32 journalDigest;
/// @notice Digest of the ordered list of `ReceiptClaim` digests corresponding to the
/// calls to `env::verify` and `env::verify_integrity`.
/// @dev Verifying the integrity of a `Receipt` corresponding to a `ReceiptClaim` with a
/// non-empty assumptions list does not guarantee unconditionally any of the claims over the
/// guest execution (i.e. if the assumptions list is non-empty, then the journal digest cannot
/// be trusted to correspond to a genuine execution). The claims can be checked by additional
/// verifying a `Receipt` for every digest in the assumptions list.
bytes32 assumptionsDigest;
}
library OutputLib {
bytes32 constant TAG_DIGEST = sha256("risc0.Output");
function digest(Output memory output) internal pure returns (bytes32) {
return sha256(
abi.encodePacked(
TAG_DIGEST,
// down
output.journalDigest,
output.assumptionsDigest,
// down.length
uint16(2) << 8
)
);
}
}
/// @notice Error raised when cryptographic verification of the zero-knowledge proof fails.
error VerificationFailed();
/// @notice Verifier interface for RISC Zero receipts of execution.
interface IRiscZeroVerifier {
/// @notice Verify that the given seal is a valid RISC Zero proof of execution with the
/// given image ID and journal digest. Reverts on failure.
/// @dev This method additionally ensures that the input hash is all-zeros (i.e. no
/// committed input), the exit code is (Halted, 0), and there are no assumptions (i.e. the
/// receipt is unconditional).
/// @param seal The encoded cryptographic proof (i.e. SNARK).
/// @param imageId The identifier for the guest program.
/// @param journalDigest The SHA-256 digest of the journal bytes.
function verify(bytes calldata seal, bytes32 imageId, bytes32 journalDigest) external view;
/// @notice Verify that the given receipt is a valid RISC Zero receipt, ensuring the `seal` is
/// valid a cryptographic proof of the execution with the given `claim`. Reverts on failure.
/// @param receipt The receipt to be verified.
function verifyIntegrity(Receipt calldata receipt) external view;
}// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.9;
/// @title Steel Library
/// @notice This library provides a collection of utilities to work with Steel commitments in Solidity.
library Steel {
/// @notice Represents a commitment to a specific block in the blockchain.
/// @dev The `id` combines the version and the actual identifier of the claim, such as the block number.
/// @dev The `digest` represents the data being committed to, e.g. the hash of the execution block.
/// @dev The `configID` is the cryptographic digest of the network configuration.
struct Commitment {
uint256 id;
bytes32 digest;
bytes32 configID;
}
/// @notice The version of the Commitment is incorrect.
error InvalidCommitmentVersion();
/// @notice The Commitment is too old and can no longer be validated.
error CommitmentTooOld();
/// @notice Validates if the provided Commitment matches the block hash of the given block number.
/// @param commitment The Commitment struct to validate.
/// @return True if the commitment's block hash matches the block hash of the block number, false otherwise.
function validateCommitment(Commitment memory commitment) internal view returns (bool) {
(uint240 claimID, uint16 version) = Encoding.decodeVersionedID(commitment.id);
if (version == 0) {
return validateBlockCommitment(claimID, commitment.digest);
} else if (version == 1) {
return validateBeaconCommitment(claimID, commitment.digest);
} else {
revert InvalidCommitmentVersion();
}
}
/// @notice Validates if the provided block commitment matches the block hash of the given block number.
/// @param blockNumber The block number to compare against.
/// @param blockHash The block hash to validate.
/// @return True if the block's block hash matches the block hash, false otherwise.
function validateBlockCommitment(uint256 blockNumber, bytes32 blockHash) internal view returns (bool) {
if (block.number - blockNumber > 256) {
revert CommitmentTooOld();
}
return blockHash == blockhash(blockNumber);
}
/// @notice Validates if the provided beacon commitment matches the block root of the given timestamp.
/// @param timestamp The timestamp to compare against.
/// @param blockRoot The block root to validate.
/// @return True if the block's block root matches the block root, false otherwise.
function validateBeaconCommitment(uint256 timestamp, bytes32 blockRoot) internal view returns (bool) {
if (block.timestamp - timestamp > 12 * 8191) {
revert CommitmentTooOld();
}
return blockRoot == Beacon.parentBlockRoot(timestamp);
}
}
/// @title Beacon Library
library Beacon {
/// @notice The address of the Beacon roots contract.
/// @dev https://eips.ethereum.org/EIPS/eip-4788
address internal constant BEACON_ROOTS_ADDRESS = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;
/// @notice Find the root of the Beacon block corresponding to the parent of the execution block with the given timestamp.
/// @return root Returns the corresponding Beacon block root or null, if no such block exists.
function parentBlockRoot(uint256 timestamp) internal view returns (bytes32 root) {
(bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(abi.encode(timestamp));
if (success) {
return abi.decode(result, (bytes32));
}
}
}
/// @title Encoding Library
library Encoding {
/// @notice Encodes a version and ID into a single uint256 value.
/// @param id The base ID to be encoded, limited by 240 bits (or the maximum value of a uint240).
/// @param version The version number to be encoded, limited by 16 bits (or the maximum value of a uint16).
/// @return Returns a single uint256 value that contains both the `id` and the `version` encoded into it.
function encodeVersionedID(uint240 id, uint16 version) internal pure returns (uint256) {
uint256 encoded;
assembly {
encoded := or(shl(240, version), id)
}
return encoded;
}
/// @notice Decodes a version and ID from a single uint256 value.
/// @param id The single uint256 value to be decoded.
/// @return Returns two values: a uint240 for the original base ID and a uint16 for the version number encoded into it.
function decodeVersionedID(uint256 id) internal pure returns (uint240, uint16) {
uint240 decoded;
uint16 version;
assembly {
decoded := and(id, 0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
version := shr(240, id)
}
return (decoded, version);
}
}// 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);
}
}// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.9;
/// @notice reverse the byte order of the uint256 value.
/// @dev Solidity uses a big-endian ABI encoding. Reversing the byte order before encoding
/// ensure that the encoded value will be little-endian.
/// Written by k06a. https://ethereum.stackexchange.com/a/83627
function reverseByteOrderUint256(uint256 input) pure returns (uint256 v) {
v = input;
// swap bytes
v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8)
| ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8);
// swap 2-byte long pairs
v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16)
| ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16);
// swap 4-byte long pairs
v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32)
| ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32);
// swap 8-byte long pairs
v = ((v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64)
| ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64);
// swap 16-byte long pairs
v = (v >> 128) | (v << 128);
}
/// @notice reverse the byte order of the uint32 value.
/// @dev Solidity uses a big-endian ABI encoding. Reversing the byte order before encoding
/// ensure that the encoded value will be little-endian.
/// Written by k06a. https://ethereum.stackexchange.com/a/83627
function reverseByteOrderUint32(uint32 input) 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);
}
/// @notice reverse the byte order of the uint16 value.
/// @dev Solidity uses a big-endian ABI encoding. Reversing the byte order before encoding
/// ensure that the encoded value will be little-endian.
/// Written by k06a. https://ethereum.stackexchange.com/a/83627
function reverseByteOrderUint16(uint16 input) pure returns (uint16 v) {
v = input;
// swap bytes
v = (v >> 8) | ((v & 0x00FF) << 8);
}// 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;
}
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"risc0/=lib/risc0-ethereum/contracts/src/",
"forge-std/=lib/forge-std/src/",
"ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"risc0-ethereum/=lib/risc0-ethereum/",
"solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"viaIR": false,
"libraries": {}
}Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"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"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"mTokenGateway_AddressNotValid","type":"error"},{"inputs":[],"name":"mTokenGateway_AmountNotValid","type":"error"},{"inputs":[],"name":"mTokenGateway_AmountTooBig","type":"error"},{"inputs":[],"name":"mTokenGateway_CallerNotAllowed","type":"error"},{"inputs":[],"name":"mTokenGateway_ChainNotValid","type":"error"},{"inputs":[],"name":"mTokenGateway_JournalNotValid","type":"error"},{"inputs":[],"name":"mTokenGateway_L1InclusionRequired","type":"error"},{"inputs":[],"name":"mTokenGateway_LengthNotValid","type":"error"},{"inputs":[],"name":"mTokenGateway_NonTransferable","type":"error"},{"inputs":[],"name":"mTokenGateway_NotEnoughGasFee","type":"error"},{"inputs":[],"name":"mTokenGateway_NotRebalancer","type":"error"},{"inputs":[{"internalType":"enum ImTokenOperationTypes.OperationType","name":"_type","type":"uint8"}],"name":"mTokenGateway_Paused","type":"error"},{"inputs":[],"name":"mTokenGateway_ReleaseCashNotAvailable","type":"error"},{"inputs":[],"name":"mTokenGateway_UserNotWhitelisted","type":"error"},{"inputs":[],"name":"mTokenProofDecoderLib_InvalidInclusion","type":"error"},{"inputs":[],"name":"mTokenProofDecoderLib_InvalidLength","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"AllowedCallerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","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":true,"internalType":"address","name":"oldVerifier","type":"address"},{"indexed":true,"internalType":"address","name":"newVerifier","type":"address"}],"name":"ZkVerifierUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"msgSender","type":"address"},{"indexed":true,"internalType":"address","name":"srcSender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"accAmountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accAmountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"srcChainId","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"dstChainId","type":"uint32"}],"name":"mTokenGateway_Extracted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mTokenGateway_GasFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"enum ImTokenOperationTypes.OperationType","name":"_type","type":"uint8"},{"indexed":false,"internalType":"bool","name":"_status","type":"bool"}],"name":"mTokenGateway_PausedState","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"msgSender","type":"address"},{"indexed":true,"internalType":"address","name":"srcSender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"accAmountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accAmountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"srcChainId","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"dstChainId","type":"uint32"}],"name":"mTokenGateway_Skipped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"accAmountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accAmountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"srcChainId","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"dstChainId","type":"uint32"},{"indexed":false,"internalType":"bytes4","name":"lineaMethodSelector","type":"bytes4"}],"name":"mTokenGateway_Supplied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"mTokenGateway_UserWhitelisted","type":"event"},{"anonymous":false,"inputs":[],"name":"mTokenGateway_WhitelistDisabled","type":"event"},{"anonymous":false,"inputs":[],"name":"mTokenGateway_WhitelistEnabled","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"accAmountIn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"accAmountOut","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowedCallers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"disableWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"enableWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"extractForRebalancing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gasFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"getProofData","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_owner","type":"address"},{"internalType":"address","name":"_underlying","type":"address"},{"internalType":"address","name":"_roles","type":"address"},{"internalType":"address","name":"zkVerifier_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum ImTokenOperationTypes.OperationType","name":"_type","type":"uint8"}],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"journalData","type":"bytes"},{"internalType":"bytes","name":"seal","type":"bytes"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"outHere","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum ImTokenOperationTypes.OperationType","name":"","type":"uint8"}],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rolesOperator","outputs":[{"internalType":"contract IRoles","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setGasFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum ImTokenOperationTypes.OperationType","name":"_type","type":"uint8"},{"internalType":"bool","name":"state","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"setUnderlying","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bool","name":"state","type":"bool"}],"name":"setWhitelistedUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes4","name":"lineaSelector","type":"bytes4"}],"name":"supplyOnHost","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"updateAllowedCallerStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_zkVerifier","type":"address"}],"name":"updateZkVerifier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"verifier","outputs":[{"internalType":"contract IZkVerifier","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"receiver","type":"address"}],"name":"withdrawGasFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
0x6080604052348015600f57600080fd5b506016601a565b60ca565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560695760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b61248a806100d96000396000f3fe6080604052600436106101b75760003560e01c8063715018a6116100ec578063cdfb2b4e1161008a578063f2fde38b11610064578063f2fde38b1461050a578063f8c8765e1461052a578063fc2e0c2f1461054a578063ffcaadfe1461057a57600080fd5b8063cdfb2b4e146104c0578063d6b0f484146104d5578063d6b457b9146104ea57600080fd5b8063966718fd116100c6578063966718fd14610433578063b511d3b114610460578063bc61e73314610480578063bdb2321f146104a057600080fd5b8063715018a6146103dc57806385eccf6c146103f15780638da5cb5b1461041e57600080fd5b80635ac86ab711610159578063678edca311610133578063678edca31461036957806368252fa7146103895780636dc59d801461039c5780636f307dc3146103bc57600080fd5b80635ac86ab7146102da578063600bb3761461030a578063658612e91461034557600080fd5b806344710fbe1161019557806344710fbe146102505780634f2be4ce146102705780634fecab701461029057806351fb012d146102b057600080fd5b80630148606c146101bc57806307d923e9146101de5780632b7ac3f314610218575b600080fd5b3480156101c857600080fd5b506101dc6101d7366004611e97565b61059a565b005b3480156101ea57600080fd5b506101fe6101f9366004611eb4565b610625565b604080519283526020830191909152015b60405180910390f35b34801561022457600080fd5b50600154610238906001600160a01b031681565b6040516001600160a01b03909116815260200161020f565b34801561025c57600080fd5b506101dc61026b366004611f04565b610652565b34801561027c57600080fd5b506101dc61028b366004611f04565b6106b9565b34801561029c57600080fd5b50600054610238906001600160a01b031681565b3480156102bc57600080fd5b506008546102ca9060ff1681565b604051901515815260200161020f565b3480156102e657600080fd5b506102ca6102f5366004611f41565b60026020526000908152604090205460ff1681565b34801561031657600080fd5b506102ca610325366004611f5c565b600660209081526000928352604080842090915290825290205460ff1681565b34801561035157600080fd5b5061035b60095481565b60405190815260200161020f565b34801561037557600080fd5b506101dc610384366004611f8a565b610725565b6101dc610397366004611fa3565b610768565b3480156103a857600080fd5b506101dc6103b7366004611ff2565b610921565b3480156103c857600080fd5b50600354610238906001600160a01b031681565b3480156103e857600080fd5b506101dc610b1a565b3480156103fd57600080fd5b5061035b61040c366004611e97565b60056020526000908152604090205481565b34801561042a57600080fd5b50610238610b2e565b34801561043f57600080fd5b5061035b61044e366004611e97565b60046020526000908152604090205481565b34801561046c57600080fd5b506101dc61047b366004612050565b610b5c565b34801561048c57600080fd5b506102ca61049b366004611f41565b610d33565b3480156104ac57600080fd5b506101dc6104bb366004611e97565b610d76565b3480156104cc57600080fd5b506101dc610da0565b3480156104e157600080fd5b506101dc610de0565b3480156104f657600080fd5b506101dc610505366004611e97565b610e1d565b34801561051657600080fd5b506101dc610525366004611e97565b610eaf565b34801561053657600080fd5b506101dc61054536600461213b565b610eed565b34801561055657600080fd5b506102ca610565366004611e97565b60076020526000908152604090205460ff1681565b34801561058657600080fd5b506101dc610595366004611f8a565b611064565b6105a26111d3565b6001600160a01b0381166105c957604051630d59e06360e11b815260040160405180910390fd5b6001546040516001600160a01b038084169216907ff335fdec5c467dfdc8bca7991b97cb3bc62c88c8467dedce3044baff0527cad690600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0382166000908152600460209081526040808320546005909252909120545b9250929050565b61065a6111d3565b6001600160a01b038216600081815260076020908152604091829020805460ff191685151590811790915591519182527f4059c608a8f17b9b24a5a29662eb74934d39ea92ae22411868f07fcac29487a0910160405180910390a25050565b3360008181526006602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917fb2cc4dde7f9044ba1999f7843e2f9cd1e4ce506f8cc2e16de26ce982bf113fa6910160405180910390a35050565b61072d6111d3565b60098190556040518181527f1208f07a7b2d478220d796eb4c732fe0d969d349f83e1fa8df282e2c995988919060200160405180910390a150565b600080805260026020527fac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c89077b54819060ff16156107c15760405163026c45ad60e41b81526004016107b891906121ad565b60405180910390fd5b50600854339060ff1615610808576001600160a01b03811660009081526007602052604090205460ff166108085760405163c29834e960e01b815260040160405180910390fd5b60008511610829576040516323366b7360e01b815260040160405180910390fd5b60095434101561084c57604051631d91909960e01b815260040160405180910390fd5b600354610864906001600160a01b0316333088611205565b6001600160a01b0384166000908152600460205260408120805487929061088c9084906121eb565b90915550506001600160a01b038416600081815260046020908152604080832054600583529281902054815193845291830191909152810187905263ffffffff4616606082015261e70560808201526001600160e01b0319851660a082015233907fc8779bd34210e0bd7bec71105864931e96b7d1e5dd1d768eedba8a9edc7a645c9060c00160405180910390a35050505050565b8015610a4f5761092f610b2e565b6001600160a01b0316336001600160a01b03161480610a2d575060005460408051632fff70a960e21b815290516001600160a01b03909216916338dd8c2c913391849163bffdc2a49160048083019260209291908290030181865afa15801561099c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c091906121fe565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015610a09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a2d9190612217565b610a4a57604051638531e41560e01b815260040160405180910390fd5b610a88565b610a57610b2e565b6001600160a01b0316336001600160a01b031614610a8857604051638531e41560e01b815260040160405180910390fd5b81600b811115610a9a57610a9a612197565b60405182151581527f2c13998e51cc6797c4e213178f18cd194f74e8438aa90df0a3a7b2f9bda5659d9060200160405180910390a2806002600084600b811115610ae657610ae6612197565b600b811115610af757610af7612197565b81526020810191909152604001600020805460ff19169115159190911790555050565b610b226111d3565b610b2c6000611272565b565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6003600081905260026020527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c354819060ff1615610bae5760405163026c45ad60e41b81526004016107b891906121ad565b506000546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c913391849163a1bd302d9160048083019260209291908290030181865afa158015610c02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2691906121fe565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015610c6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c939190612217565b610ca357610ca3888888886112e3565b6000610cb1888a018a61227b565b8051909150848114610cd657604051639a29b8a960e01b815260040160405180910390fd5b60005b8251811015610d2657610d1e838281518110610cf757610cf76123ac565b6020026020010151888884818110610d1157610d116123ac565b9050602002013587611407565b600101610cd9565b5050505050505050505050565b60006002600083600b811115610d4b57610d4b612197565b600b811115610d5c57610d5c612197565b815260208101919091526040016000205460ff1692915050565b610d7e6111d3565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b610da86111d3565b6008805460ff191660011790556040517f030c7c2cc3df831a8493f985428a9cc87a076a4204e1b038e7dd3b2bb1a018e590600090a1565b610de86111d3565b6008805460ff191690556040517f53e4b13bf8ea36dc95ec08568f4a092f872acd2708cd6dd079d74346d0b3e5cc90600090a1565b610e25610b2e565b6001600160a01b0316336001600160a01b031614158015610e545750610e5233610e4d61166a565b6116e7565b155b15610e7257604051638531e41560e01b815260040160405180910390fd5b60405147906001600160a01b0383169082156108fc029083906000818181858888f19350505050158015610eaa573d6000803e3d6000fd5b505050565b610eb76111d3565b6001600160a01b038116610ee157604051631e4fbdf760e01b8152600060048201526024016107b8565b610eea81611272565b50565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff16600081158015610f335750825b905060008267ffffffffffffffff166001148015610f505750303b155b905081158015610f5e575080155b15610f7c5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610fa657845460ff60401b1916600160401b1785555b610faf89611766565b6001600160a01b038616610fd657604051630d59e06360e11b815260040160405180910390fd5b600380546001600160a01b03808b166001600160a01b031992831617909255600080548a84169083161790556001805492891692909116919091179055831561105957845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b600b600081905260026020527fab9952baf6478d8cfb7253ce86a6c53a7b7549582c76210b1581ae682b7e556f54819060ff16156110b65760405163026c45ad60e41b81526004016107b891906121ad565b5060005460408051639e106dc760e01b815290516001600160a01b03909216916338dd8c2c9133918491639e106dc79160048083019260209291908290030181865afa15801561110a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112e91906121fe565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015611177573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119b9190612217565b6111b857604051630ee7252560e31b815260040160405180910390fd5b6003546111cf906001600160a01b03163384611777565b5050565b336111dc610b2e565b6001600160a01b031614610b2c5760405163118cdaa760e01b81523360048201526024016107b8565b6040516001600160a01b03848116602483015283811660448301526064820183905261126c9186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506117a8565b50505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b8261130157604051631abd508b60e11b815260040160405180910390fd5b600061130f8486018661227b565b9050600061131f33610e4d61180b565b80611331575061133133610e4d61185f565b9050806113995760005b825181101561139757600061136884838151811061135b5761135b6123ac565b60200260200101516118b3565b96505050505050508061138e57604051630f4bddbd60e31b815260040160405180910390fd5b5060010161133b565b505b60015460405163385db56160e01b81526001600160a01b039091169063385db561906113cf9089908990899089906004016123eb565b60006040518083038186803b1580156113e757600080fd5b505afa1580156113fb573d6000803e3d6000fd5b50505050505050505050565b6000806000806000611418886118b3565b50955095509550509450945084955061143133866119c0565b6001600160a01b038416301461145a57604051630d59e06360e11b815260040160405180910390fd5b63ffffffff821661e705146114825760405163e125bbfd60e01b815260040160405180910390fd5b4663ffffffff168163ffffffff16146114ae5760405163e125bbfd60e01b815260040160405180910390fd5b600087116114cf576040516323366b7360e01b815260040160405180910390fd5b6001600160a01b03851660009081526005602052604090205487906114f49085612412565b10156115135760405163123b28f560e01b815260040160405180910390fd5b6003546040516370a0823160e01b815230600482015288916001600160a01b0316906370a0823190602401602060405180830381865afa15801561155b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157f91906121fe565b101561159e57604051635c579b8b60e01b815260040160405180910390fd5b6001600160a01b038516600090815260056020526040812080548992906115c69084906121eb565b90915550506003546115e2906001600160a01b03168689611777565b6001600160a01b0385811660008181526004602090815260408083205460058352928190205481519384529183019190915281018a905263ffffffff8086166060830152461660808201529188169133907fc0ae438737d82fdd04b48b08fb95c82fdbc3e4c6afb08e38f567a9351397a9719060a00160405180910390a45050505050505050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166375fd4ca96040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e291906121fe565b905090565b60008054604051630e37630b60e21b81526001600160a01b03858116600483015260248201859052909116906338dd8c2c90604401602060405180830381865afa158015611739573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175d9190612217565b90505b92915050565b61176e611a66565b610eea81611aaf565b6040516001600160a01b03838116602483015260448201839052610eaa91859182169063a9059cbb9060640161123a565b60006117bd6001600160a01b03841683611ab7565b905080516000141580156117e25750808060200190518101906117e09190612217565b155b15610eaa57604051635274afe760e01b81526001600160a01b03841660048201526024016107b8565b60008060009054906101000a90046001600160a01b03166001600160a01b031663a87201956040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116be573d6000803e3d6000fd5b60008060009054906101000a90046001600160a01b03166001600160a01b031663a1bd302d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116be573d6000803e3d6000fd5b600080600080600080600060718851146118e05760405163e12a431160e01b815260040160405180910390fd5b6118f76118f08960006014611ac5565b6000611bd4565b96506119086118f089601480611ac5565b955061192161191a8960286020611ac5565b6000611c39565b945061193361191a8960486020611ac5565b935061194c6119458960686004611ac5565b6000611c97565b925061195e61194589606c6004611ac5565b915060006119796119728a60706001611ac5565b6000611cf4565b905060ff8116158061198e57508060ff166001145b6119ab5760405163a991c51f60e01b815260040160405180910390fd5b8060ff16600114915050919395979092949650565b806001600160a01b0316826001600160a01b0316146111cf576001600160a01b0380821660009081526006602090815260408083209386168352929052205460ff1680611a255750611a10610b2e565b6001600160a01b0316826001600160a01b0316145b80611a375750611a3782610e4d61180b565b80611a495750611a4982610e4d61185f565b6111cf57604051638531e41560e01b815260040160405180910390fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16610b2c57604051631afcd79f60e31b815260040160405180910390fd5b610eb7611a66565b606061175d83836000611d50565b606081611ad381601f6121eb565b1015611b125760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b60448201526064016107b8565b611b1c82846121eb565b84511015611b605760405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b60448201526064016107b8565b606082158015611b7f5760405191506000825260208201604052611bc9565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015611bb8578051835260209283019201611ba0565b5050858452601f01601f1916604052505b5090505b9392505050565b6000611be18260146121eb565b83511015611c295760405162461bcd60e51b8152602060048201526015602482015274746f416464726573735f6f75744f66426f756e647360581b60448201526064016107b8565b500160200151600160601b900490565b6000611c468260206121eb565b83511015611c8e5760405162461bcd60e51b8152602060048201526015602482015274746f55696e743235365f6f75744f66426f756e647360581b60448201526064016107b8565b50016020015190565b6000611ca48260046121eb565b83511015611ceb5760405162461bcd60e51b8152602060048201526014602482015273746f55696e7433325f6f75744f66426f756e647360601b60448201526064016107b8565b50016004015190565b6000611d018260016121eb565b83511015611d475760405162461bcd60e51b8152602060048201526013602482015272746f55696e74385f6f75744f66426f756e647360681b60448201526064016107b8565b50016001015190565b606081471015611d755760405163cd78605960e01b81523060048201526024016107b8565b600080856001600160a01b03168486604051611d919190612425565b60006040518083038185875af1925050503d8060008114611dce576040519150601f19603f3d011682016040523d82523d6000602084013e611dd3565b606091505b5091509150611de3868383611ded565b9695505050505050565b606082611e0257611dfd82611e49565b611bcd565b8151158015611e1957506001600160a01b0384163b155b15611e4257604051639996b31560e01b81526001600160a01b03851660048201526024016107b8565b5080611bcd565b805115611e595780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0381168114610eea57600080fd5b8035611e9281611e72565b919050565b600060208284031215611ea957600080fd5b8135611bcd81611e72565b60008060408385031215611ec757600080fd5b8235611ed281611e72565b9150602083013563ffffffff81168114611eeb57600080fd5b809150509250929050565b8015158114610eea57600080fd5b60008060408385031215611f1757600080fd5b8235611f2281611e72565b91506020830135611eeb81611ef6565b8035600c8110611e9257600080fd5b600060208284031215611f5357600080fd5b61175d82611f32565b60008060408385031215611f6f57600080fd5b8235611f7a81611e72565b91506020830135611eeb81611e72565b600060208284031215611f9c57600080fd5b5035919050565b600080600060608486031215611fb857600080fd5b833592506020840135611fca81611e72565b915060408401356001600160e01b031981168114611fe757600080fd5b809150509250925092565b6000806040838503121561200557600080fd5b611f2283611f32565b60008083601f84011261202057600080fd5b50813567ffffffffffffffff81111561203857600080fd5b60208301915083602082850101111561064b57600080fd5b60008060008060008060006080888a03121561206b57600080fd5b873567ffffffffffffffff81111561208257600080fd5b61208e8a828b0161200e565b909850965050602088013567ffffffffffffffff8111156120ae57600080fd5b6120ba8a828b0161200e565b909650945050604088013567ffffffffffffffff8111156120da57600080fd5b8801601f81018a136120eb57600080fd5b803567ffffffffffffffff81111561210257600080fd5b8a60208260051b840101111561211757600080fd5b6020919091019350915061212d60608901611e87565b905092959891949750929550565b6000806000806080858703121561215157600080fd5b843561215c81611e72565b9350602085013561216c81611e72565b9250604085013561217c81611e72565b9150606085013561218c81611e72565b939692955090935050565b634e487b7160e01b600052602160045260246000fd5b60208101600c83106121cf57634e487b7160e01b600052602160045260246000fd5b91905290565b634e487b7160e01b600052601160045260246000fd5b80820180821115611760576117606121d5565b60006020828403121561221057600080fd5b5051919050565b60006020828403121561222957600080fd5b8151611bcd81611ef6565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561227357612273612234565b604052919050565b60006020828403121561228d57600080fd5b813567ffffffffffffffff8111156122a457600080fd5b8201601f810184136122b557600080fd5b803567ffffffffffffffff8111156122cf576122cf612234565b8060051b6122df6020820161224a565b918252602081840181019290810190878411156122fb57600080fd5b6020850192505b838310156123a157823567ffffffffffffffff81111561232157600080fd5b8501603f8101891361233257600080fd5b602081013567ffffffffffffffff81111561234f5761234f612234565b612362601f8201601f191660200161224a565b8181526040838301018b101561237757600080fd5b81604084016020830137600060208383010152808552505050602082019150602083019250612302565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6040815260006123ff6040830186886123c2565b82810360208401526123a18185876123c2565b81810381811115611760576117606121d5565b6000825160005b81811015612446576020818601810151858301520161242c565b50600092019182525091905056fea26469706673582212208609d0c0b17f36122e5b4e5c388f9367934891d49d9a92e7b3a74ab8ebec6be364736f6c634300081c0033
Deployed Bytecode
0x6080604052600436106101b75760003560e01c8063715018a6116100ec578063cdfb2b4e1161008a578063f2fde38b11610064578063f2fde38b1461050a578063f8c8765e1461052a578063fc2e0c2f1461054a578063ffcaadfe1461057a57600080fd5b8063cdfb2b4e146104c0578063d6b0f484146104d5578063d6b457b9146104ea57600080fd5b8063966718fd116100c6578063966718fd14610433578063b511d3b114610460578063bc61e73314610480578063bdb2321f146104a057600080fd5b8063715018a6146103dc57806385eccf6c146103f15780638da5cb5b1461041e57600080fd5b80635ac86ab711610159578063678edca311610133578063678edca31461036957806368252fa7146103895780636dc59d801461039c5780636f307dc3146103bc57600080fd5b80635ac86ab7146102da578063600bb3761461030a578063658612e91461034557600080fd5b806344710fbe1161019557806344710fbe146102505780634f2be4ce146102705780634fecab701461029057806351fb012d146102b057600080fd5b80630148606c146101bc57806307d923e9146101de5780632b7ac3f314610218575b600080fd5b3480156101c857600080fd5b506101dc6101d7366004611e97565b61059a565b005b3480156101ea57600080fd5b506101fe6101f9366004611eb4565b610625565b604080519283526020830191909152015b60405180910390f35b34801561022457600080fd5b50600154610238906001600160a01b031681565b6040516001600160a01b03909116815260200161020f565b34801561025c57600080fd5b506101dc61026b366004611f04565b610652565b34801561027c57600080fd5b506101dc61028b366004611f04565b6106b9565b34801561029c57600080fd5b50600054610238906001600160a01b031681565b3480156102bc57600080fd5b506008546102ca9060ff1681565b604051901515815260200161020f565b3480156102e657600080fd5b506102ca6102f5366004611f41565b60026020526000908152604090205460ff1681565b34801561031657600080fd5b506102ca610325366004611f5c565b600660209081526000928352604080842090915290825290205460ff1681565b34801561035157600080fd5b5061035b60095481565b60405190815260200161020f565b34801561037557600080fd5b506101dc610384366004611f8a565b610725565b6101dc610397366004611fa3565b610768565b3480156103a857600080fd5b506101dc6103b7366004611ff2565b610921565b3480156103c857600080fd5b50600354610238906001600160a01b031681565b3480156103e857600080fd5b506101dc610b1a565b3480156103fd57600080fd5b5061035b61040c366004611e97565b60056020526000908152604090205481565b34801561042a57600080fd5b50610238610b2e565b34801561043f57600080fd5b5061035b61044e366004611e97565b60046020526000908152604090205481565b34801561046c57600080fd5b506101dc61047b366004612050565b610b5c565b34801561048c57600080fd5b506102ca61049b366004611f41565b610d33565b3480156104ac57600080fd5b506101dc6104bb366004611e97565b610d76565b3480156104cc57600080fd5b506101dc610da0565b3480156104e157600080fd5b506101dc610de0565b3480156104f657600080fd5b506101dc610505366004611e97565b610e1d565b34801561051657600080fd5b506101dc610525366004611e97565b610eaf565b34801561053657600080fd5b506101dc61054536600461213b565b610eed565b34801561055657600080fd5b506102ca610565366004611e97565b60076020526000908152604090205460ff1681565b34801561058657600080fd5b506101dc610595366004611f8a565b611064565b6105a26111d3565b6001600160a01b0381166105c957604051630d59e06360e11b815260040160405180910390fd5b6001546040516001600160a01b038084169216907ff335fdec5c467dfdc8bca7991b97cb3bc62c88c8467dedce3044baff0527cad690600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0382166000908152600460209081526040808320546005909252909120545b9250929050565b61065a6111d3565b6001600160a01b038216600081815260076020908152604091829020805460ff191685151590811790915591519182527f4059c608a8f17b9b24a5a29662eb74934d39ea92ae22411868f07fcac29487a0910160405180910390a25050565b3360008181526006602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917fb2cc4dde7f9044ba1999f7843e2f9cd1e4ce506f8cc2e16de26ce982bf113fa6910160405180910390a35050565b61072d6111d3565b60098190556040518181527f1208f07a7b2d478220d796eb4c732fe0d969d349f83e1fa8df282e2c995988919060200160405180910390a150565b600080805260026020527fac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c89077b54819060ff16156107c15760405163026c45ad60e41b81526004016107b891906121ad565b60405180910390fd5b50600854339060ff1615610808576001600160a01b03811660009081526007602052604090205460ff166108085760405163c29834e960e01b815260040160405180910390fd5b60008511610829576040516323366b7360e01b815260040160405180910390fd5b60095434101561084c57604051631d91909960e01b815260040160405180910390fd5b600354610864906001600160a01b0316333088611205565b6001600160a01b0384166000908152600460205260408120805487929061088c9084906121eb565b90915550506001600160a01b038416600081815260046020908152604080832054600583529281902054815193845291830191909152810187905263ffffffff4616606082015261e70560808201526001600160e01b0319851660a082015233907fc8779bd34210e0bd7bec71105864931e96b7d1e5dd1d768eedba8a9edc7a645c9060c00160405180910390a35050505050565b8015610a4f5761092f610b2e565b6001600160a01b0316336001600160a01b03161480610a2d575060005460408051632fff70a960e21b815290516001600160a01b03909216916338dd8c2c913391849163bffdc2a49160048083019260209291908290030181865afa15801561099c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c091906121fe565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015610a09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a2d9190612217565b610a4a57604051638531e41560e01b815260040160405180910390fd5b610a88565b610a57610b2e565b6001600160a01b0316336001600160a01b031614610a8857604051638531e41560e01b815260040160405180910390fd5b81600b811115610a9a57610a9a612197565b60405182151581527f2c13998e51cc6797c4e213178f18cd194f74e8438aa90df0a3a7b2f9bda5659d9060200160405180910390a2806002600084600b811115610ae657610ae6612197565b600b811115610af757610af7612197565b81526020810191909152604001600020805460ff19169115159190911790555050565b610b226111d3565b610b2c6000611272565b565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6003600081905260026020527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c354819060ff1615610bae5760405163026c45ad60e41b81526004016107b891906121ad565b506000546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c913391849163a1bd302d9160048083019260209291908290030181865afa158015610c02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2691906121fe565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015610c6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c939190612217565b610ca357610ca3888888886112e3565b6000610cb1888a018a61227b565b8051909150848114610cd657604051639a29b8a960e01b815260040160405180910390fd5b60005b8251811015610d2657610d1e838281518110610cf757610cf76123ac565b6020026020010151888884818110610d1157610d116123ac565b9050602002013587611407565b600101610cd9565b5050505050505050505050565b60006002600083600b811115610d4b57610d4b612197565b600b811115610d5c57610d5c612197565b815260208101919091526040016000205460ff1692915050565b610d7e6111d3565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b610da86111d3565b6008805460ff191660011790556040517f030c7c2cc3df831a8493f985428a9cc87a076a4204e1b038e7dd3b2bb1a018e590600090a1565b610de86111d3565b6008805460ff191690556040517f53e4b13bf8ea36dc95ec08568f4a092f872acd2708cd6dd079d74346d0b3e5cc90600090a1565b610e25610b2e565b6001600160a01b0316336001600160a01b031614158015610e545750610e5233610e4d61166a565b6116e7565b155b15610e7257604051638531e41560e01b815260040160405180910390fd5b60405147906001600160a01b0383169082156108fc029083906000818181858888f19350505050158015610eaa573d6000803e3d6000fd5b505050565b610eb76111d3565b6001600160a01b038116610ee157604051631e4fbdf760e01b8152600060048201526024016107b8565b610eea81611272565b50565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff16600081158015610f335750825b905060008267ffffffffffffffff166001148015610f505750303b155b905081158015610f5e575080155b15610f7c5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610fa657845460ff60401b1916600160401b1785555b610faf89611766565b6001600160a01b038616610fd657604051630d59e06360e11b815260040160405180910390fd5b600380546001600160a01b03808b166001600160a01b031992831617909255600080548a84169083161790556001805492891692909116919091179055831561105957845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b600b600081905260026020527fab9952baf6478d8cfb7253ce86a6c53a7b7549582c76210b1581ae682b7e556f54819060ff16156110b65760405163026c45ad60e41b81526004016107b891906121ad565b5060005460408051639e106dc760e01b815290516001600160a01b03909216916338dd8c2c9133918491639e106dc79160048083019260209291908290030181865afa15801561110a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112e91906121fe565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015611177573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119b9190612217565b6111b857604051630ee7252560e31b815260040160405180910390fd5b6003546111cf906001600160a01b03163384611777565b5050565b336111dc610b2e565b6001600160a01b031614610b2c5760405163118cdaa760e01b81523360048201526024016107b8565b6040516001600160a01b03848116602483015283811660448301526064820183905261126c9186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506117a8565b50505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b8261130157604051631abd508b60e11b815260040160405180910390fd5b600061130f8486018661227b565b9050600061131f33610e4d61180b565b80611331575061133133610e4d61185f565b9050806113995760005b825181101561139757600061136884838151811061135b5761135b6123ac565b60200260200101516118b3565b96505050505050508061138e57604051630f4bddbd60e31b815260040160405180910390fd5b5060010161133b565b505b60015460405163385db56160e01b81526001600160a01b039091169063385db561906113cf9089908990899089906004016123eb565b60006040518083038186803b1580156113e757600080fd5b505afa1580156113fb573d6000803e3d6000fd5b50505050505050505050565b6000806000806000611418886118b3565b50955095509550509450945084955061143133866119c0565b6001600160a01b038416301461145a57604051630d59e06360e11b815260040160405180910390fd5b63ffffffff821661e705146114825760405163e125bbfd60e01b815260040160405180910390fd5b4663ffffffff168163ffffffff16146114ae5760405163e125bbfd60e01b815260040160405180910390fd5b600087116114cf576040516323366b7360e01b815260040160405180910390fd5b6001600160a01b03851660009081526005602052604090205487906114f49085612412565b10156115135760405163123b28f560e01b815260040160405180910390fd5b6003546040516370a0823160e01b815230600482015288916001600160a01b0316906370a0823190602401602060405180830381865afa15801561155b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157f91906121fe565b101561159e57604051635c579b8b60e01b815260040160405180910390fd5b6001600160a01b038516600090815260056020526040812080548992906115c69084906121eb565b90915550506003546115e2906001600160a01b03168689611777565b6001600160a01b0385811660008181526004602090815260408083205460058352928190205481519384529183019190915281018a905263ffffffff8086166060830152461660808201529188169133907fc0ae438737d82fdd04b48b08fb95c82fdbc3e4c6afb08e38f567a9351397a9719060a00160405180910390a45050505050505050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166375fd4ca96040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e291906121fe565b905090565b60008054604051630e37630b60e21b81526001600160a01b03858116600483015260248201859052909116906338dd8c2c90604401602060405180830381865afa158015611739573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175d9190612217565b90505b92915050565b61176e611a66565b610eea81611aaf565b6040516001600160a01b03838116602483015260448201839052610eaa91859182169063a9059cbb9060640161123a565b60006117bd6001600160a01b03841683611ab7565b905080516000141580156117e25750808060200190518101906117e09190612217565b155b15610eaa57604051635274afe760e01b81526001600160a01b03841660048201526024016107b8565b60008060009054906101000a90046001600160a01b03166001600160a01b031663a87201956040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116be573d6000803e3d6000fd5b60008060009054906101000a90046001600160a01b03166001600160a01b031663a1bd302d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116be573d6000803e3d6000fd5b600080600080600080600060718851146118e05760405163e12a431160e01b815260040160405180910390fd5b6118f76118f08960006014611ac5565b6000611bd4565b96506119086118f089601480611ac5565b955061192161191a8960286020611ac5565b6000611c39565b945061193361191a8960486020611ac5565b935061194c6119458960686004611ac5565b6000611c97565b925061195e61194589606c6004611ac5565b915060006119796119728a60706001611ac5565b6000611cf4565b905060ff8116158061198e57508060ff166001145b6119ab5760405163a991c51f60e01b815260040160405180910390fd5b8060ff16600114915050919395979092949650565b806001600160a01b0316826001600160a01b0316146111cf576001600160a01b0380821660009081526006602090815260408083209386168352929052205460ff1680611a255750611a10610b2e565b6001600160a01b0316826001600160a01b0316145b80611a375750611a3782610e4d61180b565b80611a495750611a4982610e4d61185f565b6111cf57604051638531e41560e01b815260040160405180910390fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16610b2c57604051631afcd79f60e31b815260040160405180910390fd5b610eb7611a66565b606061175d83836000611d50565b606081611ad381601f6121eb565b1015611b125760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b60448201526064016107b8565b611b1c82846121eb565b84511015611b605760405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b60448201526064016107b8565b606082158015611b7f5760405191506000825260208201604052611bc9565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015611bb8578051835260209283019201611ba0565b5050858452601f01601f1916604052505b5090505b9392505050565b6000611be18260146121eb565b83511015611c295760405162461bcd60e51b8152602060048201526015602482015274746f416464726573735f6f75744f66426f756e647360581b60448201526064016107b8565b500160200151600160601b900490565b6000611c468260206121eb565b83511015611c8e5760405162461bcd60e51b8152602060048201526015602482015274746f55696e743235365f6f75744f66426f756e647360581b60448201526064016107b8565b50016020015190565b6000611ca48260046121eb565b83511015611ceb5760405162461bcd60e51b8152602060048201526014602482015273746f55696e7433325f6f75744f66426f756e647360601b60448201526064016107b8565b50016004015190565b6000611d018260016121eb565b83511015611d475760405162461bcd60e51b8152602060048201526013602482015272746f55696e74385f6f75744f66426f756e647360681b60448201526064016107b8565b50016001015190565b606081471015611d755760405163cd78605960e01b81523060048201526024016107b8565b600080856001600160a01b03168486604051611d919190612425565b60006040518083038185875af1925050503d8060008114611dce576040519150601f19603f3d011682016040523d82523d6000602084013e611dd3565b606091505b5091509150611de3868383611ded565b9695505050505050565b606082611e0257611dfd82611e49565b611bcd565b8151158015611e1957506001600160a01b0384163b155b15611e4257604051639996b31560e01b81526001600160a01b03851660048201526024016107b8565b5080611bcd565b805115611e595780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0381168114610eea57600080fd5b8035611e9281611e72565b919050565b600060208284031215611ea957600080fd5b8135611bcd81611e72565b60008060408385031215611ec757600080fd5b8235611ed281611e72565b9150602083013563ffffffff81168114611eeb57600080fd5b809150509250929050565b8015158114610eea57600080fd5b60008060408385031215611f1757600080fd5b8235611f2281611e72565b91506020830135611eeb81611ef6565b8035600c8110611e9257600080fd5b600060208284031215611f5357600080fd5b61175d82611f32565b60008060408385031215611f6f57600080fd5b8235611f7a81611e72565b91506020830135611eeb81611e72565b600060208284031215611f9c57600080fd5b5035919050565b600080600060608486031215611fb857600080fd5b833592506020840135611fca81611e72565b915060408401356001600160e01b031981168114611fe757600080fd5b809150509250925092565b6000806040838503121561200557600080fd5b611f2283611f32565b60008083601f84011261202057600080fd5b50813567ffffffffffffffff81111561203857600080fd5b60208301915083602082850101111561064b57600080fd5b60008060008060008060006080888a03121561206b57600080fd5b873567ffffffffffffffff81111561208257600080fd5b61208e8a828b0161200e565b909850965050602088013567ffffffffffffffff8111156120ae57600080fd5b6120ba8a828b0161200e565b909650945050604088013567ffffffffffffffff8111156120da57600080fd5b8801601f81018a136120eb57600080fd5b803567ffffffffffffffff81111561210257600080fd5b8a60208260051b840101111561211757600080fd5b6020919091019350915061212d60608901611e87565b905092959891949750929550565b6000806000806080858703121561215157600080fd5b843561215c81611e72565b9350602085013561216c81611e72565b9250604085013561217c81611e72565b9150606085013561218c81611e72565b939692955090935050565b634e487b7160e01b600052602160045260246000fd5b60208101600c83106121cf57634e487b7160e01b600052602160045260246000fd5b91905290565b634e487b7160e01b600052601160045260246000fd5b80820180821115611760576117606121d5565b60006020828403121561221057600080fd5b5051919050565b60006020828403121561222957600080fd5b8151611bcd81611ef6565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561227357612273612234565b604052919050565b60006020828403121561228d57600080fd5b813567ffffffffffffffff8111156122a457600080fd5b8201601f810184136122b557600080fd5b803567ffffffffffffffff8111156122cf576122cf612234565b8060051b6122df6020820161224a565b918252602081840181019290810190878411156122fb57600080fd5b6020850192505b838310156123a157823567ffffffffffffffff81111561232157600080fd5b8501603f8101891361233257600080fd5b602081013567ffffffffffffffff81111561234f5761234f612234565b612362601f8201601f191660200161224a565b8181526040838301018b101561237757600080fd5b81604084016020830137600060208383010152808552505050602082019150602083019250612302565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6040815260006123ff6040830186886123c2565b82810360208401526123a18185876123c2565b81810381811115611760576117606121d5565b6000825160005b81811015612446576020818601810151858301520161242c565b50600092019182525091905056fea26469706673582212208609d0c0b17f36122e5b4e5c388f9367934891d49d9a92e7b3a74ab8ebec6be364736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.