Repository navigation
[Contract Verification] How to verify contract with library build with solc 0.8.17 #1136
Team or ProjectNo response ZK chainEra EnvironmentMainnet Block ExplorerZKsync explorer (e.g. https://explorer.zksync.io) Issue Type
Contract Address0x7a7ad9aa93cd0a2d0255326e5fb145cec14997ff Compiler TypeSingle file zkSolc VersionNo Solc Version0.8.17 Contract NameDexRouter Contract Code// SPDX-License-Identifier: MIT
pragma solidity =0.8.17 ^0.8.0;
pragma abicoder v2;
// contracts/8/libraries/Address.sol
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly {
codehash := extcodehash(account)
}
return (codehash != accountHash && codehash != 0x0);
}
/**
* @dev Converts an `address` into `address payable`. Note that this is
* simply a type cast: the actual underlying value is not changed.
*
* _Available since v2.4.0._
*/
function toPayable(address account)
internal
pure
returns (address payable)
{
return payable(account);
}
/**
* @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*
* _Available since v2.4.0._
*/
function sendValue(address recipient, uint256 amount) internal {
require(
address(this).balance >= amount,
"Address: insufficient balance"
);
// solhint-disable-next-line avoid-call-value
(bool success, ) = recipient.call{value: amount}("");
require(
success,
"Address: unable to send value, recipient may have reverted"
);
}
}
// contracts/8/libraries/Constants.sol
// =============================================================================
// ============================ ETH Address Constants ==========================
// =============================================================================
address constant _ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
// =============================================================================
// ======================= Universal Masks And Flags ===========================
// =============================================================================
uint256 constant _ADDRESS_MASK =
0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
uint256 constant _REVERSE_MASK =
0x8000000000000000000000000000000000000000000000000000000000000000;
uint256 constant _ORDER_ID_MASK =
0xffffffffffffffffffffffff0000000000000000000000000000000000000000;
uint256 constant _WEIGHT_MASK =
0x00000000000000000000ffff0000000000000000000000000000000000000000;
uint256 constant _CALL_GAS_LIMIT = 5000;
uint256 constant ORIGIN_PAYER =
0x3ca20afc2ccc0000000000000000000000000000000000000000000000000000;
uint256 constant SWAP_AMOUNT =
0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff;
uint256 constant _WETH_MASK =
0x4000000000000000000000000000000000000000000000000000000000000000;
uint256 constant _ONE_FOR_ZERO_MASK = 1 << 255; // Mask for identifying if the swap is one-for-zero
uint256 constant _WETH_UNWRAP_MASK = 1 << 253; // Mask for identifying if WETH should be unwrapped to ETH
// =============================================================================
// ===================== Transfer Mode Masks and Flags =========================
// =============================================================================
uint256 constant _MODE_LEGACY =
0x0000000000000000000000000000000000000000000000000000000000000000; // 0b000 in [bit251, bit250, bit249]
uint256 constant _MODE_PERMIT2_ALLOWANCE =
0x0200000000000000000000000000000000000000000000000000000000000000; // 0b001 in [bit251, bit250, bit249]
uint256 constant _MODE_PERMIT2_SIGNATURE =
0x0600000000000000000000000000000000000000000000000000000000000000; // 0b011 in [bit251, bit250, bit249]
uint256 constant _MODE_BY_INVEST =
0x0400000000000000000000000000000000000000000000000000000000000000; // 0b010 in [bit251, bit250, bit249]
uint256 constant _MODE_NO_TRANSFER =
0x0800000000000000000000000000000000000000000000000000000000000000; // 0b100 in [bit251, bit250, bit249]
uint256 constant _MODE_DIRECT =
0x0A00000000000000000000000000000000000000000000000000000000000000; // 0b101 in [bit251, bit250, bit249]
uint256 constant _TRANSFER_MODE_MASK =
0x0E00000000000000000000000000000000000000000000000000000000000000; // 0b111 in [bit251, bit250, bit249]
// =============================================================================
// ============================== DAG Swap Masks ===============================
// =============================================================================
uint256 constant _INPUT_INDEX_MASK =
0x0000000000000000ff0000000000000000000000000000000000000000000000;
uint256 constant _OUTPUT_INDEX_MASK =
0x000000000000000000ff00000000000000000000000000000000000000000000;
// =============================================================================
// ======================= Commission Masks and Flags ==========================
// =============================================================================
uint256 constant _COMMISSION_RATE_MASK =
0x000000000000ffffffffffff0000000000000000000000000000000000000000;
uint256 constant _COMMISSION_FLAG_MASK =
0xffffffffffff0000000000000000000000000000000000000000000000000000;
uint256 constant FROM_TOKEN_COMMISSION =
0x3ca20afc2aaa0000000000000000000000000000000000000000000000000000;
uint256 constant TO_TOKEN_COMMISSION =
0x3ca20afc2bbb0000000000000000000000000000000000000000000000000000;
uint256 constant FROM_TOKEN_COMMISSION_DUAL =
0x22220afc2aaa0000000000000000000000000000000000000000000000000000;
uint256 constant TO_TOKEN_COMMISSION_DUAL =
0x22220afc2bbb0000000000000000000000000000000000000000000000000000;
uint256 constant FROM_TOKEN_COMMISSION_MULTIPLE =
0x88880afc2aaa0000000000000000000000000000000000000000000000000000;
uint256 constant TO_TOKEN_COMMISSION_MULTIPLE =
0x88880afc2bbb0000000000000000000000000000000000000000000000000000;
uint256 constant _COMMISSION_LENGTH_MASK =
0x00ff000000000000000000000000000000000000000000000000000000000000;
uint256 constant _TO_B_COMMISSION_MASK =
0x8000000000000000000000000000000000000000000000000000000000000000;
// =============================================================================
// ============================= Trim Masks and Flags ==========================
// =============================================================================
uint256 constant _TRIM_FLAG_MASK =
0xffffffffffff0000000000000000000000000000000000000000000000000000;
uint256 constant _TRIM_EXPECT_AMOUNT_OUT_OR_ADDRESS_MASK =
0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
uint256 constant _TRIM_RATE_MASK =
0x000000000000ffffffffffff0000000000000000000000000000000000000000;
uint256 constant _TO_B_TRIM_MASK =
0x0000000000008000000000000000000000000000000000000000000000000000;
uint256 constant TRIM_FLAG =
0x7777777711110000000000000000000000000000000000000000000000000000;
uint256 constant TRIM_DUAL_FLAG =
0x7777777722220000000000000000000000000000000000000000000000000000;
// =============================================================================
// ======================== Permit2 Info Masks and Flags =======================
// =============================================================================
uint256 constant _PERMIT2_INFO_FLAG_MASK =
0xffffffffffff0000000000000000000000000000000000000000000000000000;
uint256 constant _PERMIT2_INFO_AMOUNT_LENGTH_MASK =
0x000000000000ff00000000000000000000000000000000000000000000000000;
uint256 constant _PERMIT2_INFO_NONCE_MASK =
0x0000000000000000000000000000000000000000000000000000ffffffffffff;
uint256 constant _PERMIT2_INFO_DEADLINE_MASK =
0x0000000000000000000000000000000000000000ffffffffffff000000000000;
uint256 constant _PERMIT2_INFO_SIGNATURE_V_MASK =
0x00000000000000000000000000000000000000ff000000000000000000000000;
uint256 constant PERMIT2_INFO_SIG65_FLAG =
0x92527fff1a650000000000000000000000000000000000000000000000000000; // bytes5(keccak256("Permit2Info")) + 0x65
uint256 constant PERMIT2_INFO_SIG64_FLAG =
0x92527fff1a640000000000000000000000000000000000000000000000000000; // bytes5(keccak256("Permit2Info")) + 0x64
// =============================================================================
// ============================= RefundTo Masks and Flags ======================
// =============================================================================
uint256 constant _REFUND_TO_MASK =
0xffffffffffff0000000000000000000000000000000000000000000000000000;
uint256 constant REFUND_TO_ADDRESS_FLAG =
0xac3a7da8b1c60000000000000000000000000000000000000000000000000000; // bytes6(keccak256("RefundTo"))
// =============================================================================
// ==================== UniswapV3 Callback Flags ===============================
// =============================================================================
bytes32 constant V3_EXACT_IN_CALLBACK_FLAG = 0x97d5390607e40000000000000000000000000000000000000000000000000000; // bytes6(keccak256("V3ExactInCallback"))
bytes32 constant V3_EXACT_OUT_CALLBACK_FLAG = 0xae25331d0e6e0000000000000000000000000000000000000000000000000000; // bytes6(keccak256("V3ExactOutCallback"))
// =============================================================================
// ============================= Universal Values ==============================
// =============================================================================
uint256 constant MIN_COMMISSION_MULTIPLE_NUM = 3; // min referrer num for multiple commission
uint256 constant MAX_COMMISSION_MULTIPLE_NUM = 8; // max referrer num for multiple commission
uint256 constant COMMISSION_RATE_LIMIT = 10000000;
uint256 constant DENOMINATOR = 10 ** 9;
uint256 constant NO_TO_B_MODE = 1; // value for no-toB commission and no-toB trim when related calldata exists
uint256 constant TO_B_MODE = 2; // value for toB commission and toB trim when related calldata exists
uint256 constant WAD = 1 * 10 ** 18;
uint256 constant TRIM_RATE_LIMIT = 100;
uint256 constant TRIM_DENOMINATOR = 1000;
uint256 constant NATIVE_TOKEN_TRANSFER_GAS_LIMIT = 100000;
// =============================================================================
// ==================== Network-Specific Addresses =============================
// =============================================================================
// NOTE: These addresses need to be changed before deployment to different networks
address constant _WETH = 0x0000000000000000000000000000000000000000;
address constant _APPROVE_PROXY = 0x0000000000000000000000000000000000000000;
address constant _PERMIT2 = 0x0000000000000000000000000000000000000000;
// contracts/8/libraries/EventLib.sol
/// @title EventLib
/// @notice The single source of truth for all DexRouter event definitions in this repository.
/// @dev This library centralizes DexRouter events to ensure consistency across all contracts and libraries.
/// Other contracts/router should use the emit functions provided here instead of defining events locally.
library EventLib {
/// ==============================================================
/// ========================= Events =============================
/// ==============================================================
event OrderRecord(
address fromToken,
address toToken,
address sender,
uint256 fromAmount,
uint256 returnAmount
);
event SwapOrderId(uint256 id);
event CommissionAndTrimInfo(
uint256 toBCommission, // 0 for no encoded commission data, 1 for no-toB commission, 2 for toB commission
uint256 toBTrim, // 0 for no encoded trim data, 1 for no-toB trim, 2 for toB trim
uint256 trimRate,
uint256 chargeRate
);
// @notice CommissionFromTokenRecord is emitted in assembly, commentted out for contract size saving
// event CommissionFromTokenRecord(
// address fromTokenAddress,
// uint256 commissionAmount,
// address referrerAddress,
// uint256 commissionRate
// );
// @notice CommissionToTokenRecord is emitted in assembly, commentted out for contract size saving
// event CommissionToTokenRecord(
// address toTokenAddress,
// uint256 commissionAmount,
// address referrerAddress,
// uint256 commissionRate
// );
// @notice PositiveSlippageTrimRecord is emitted in assembly, commentted out for contract size saving
// event PositiveSlippageTrimRecord(
// address toTokenAddress,
// uint256 trimAmount,
// address trimAddress
// );
// @notice PositiveSlippageChargeRecord is emitted in assembly, commentted out for contract size saving
// event PositiveSlippageChargeRecord(
// address toTokenAddress,
// uint256 chargeAmount,
// address chargeAddress
// );
/// ==============================================================
/// ====================== Emit Functions ========================
/// ==============================================================
function emitOrderRecord(
address fromToken,
address toToken,
address sender,
uint256 fromAmount,
uint256 returnAmount
) internal {
emit OrderRecord(fromToken, toToken, sender, fromAmount, returnAmount);
}
function emitSwapOrderId(uint256 id) internal {
emit SwapOrderId(id);
}
function emitCommissionAndTrimInfo(
uint256 toBCommission,
uint256 toBTrim,
uint256 trimRate,
uint256 chargeRate
) internal {
emit CommissionAndTrimInfo(toBCommission, toBTrim, trimRate, chargeRate);
}
}
// contracts/8/interfaces/IAdapter.sol
interface IAdapter {
function sellBase(
address to,
address pool,
bytes memory data
) external;
function sellQuote(
address to,
address pool,
bytes memory data
) external;
}
// contracts/8/interfaces/IApproveProxy.sol
interface IApproveProxy {
function isAllowedProxy(address _proxy) external view returns (bool);
function claimTokens(
address token,
address who,
address dest,
uint256 amount
) external;
function tokenApprove() external view returns (address);
function addProxy(address) external;
}
// contracts/8/interfaces/IDaiLikePermit.sol
/// @title Interface for DAI-style permits
interface IDaiLikePermit {
function permit(
address holder,
address spender,
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
// contracts/8/interfaces/IERC20.sol
interface IERC20 {
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
event Transfer(address indexed from, address indexed to, uint256 value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function allowance(address owner, address spender)
external
view
returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool);
}
// contracts/8/interfaces/IERC20Permit.sol
/**
* @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.
*/
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].
*/
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);
}
// contracts/8/interfaces/IPermit2.sol
interface IPermit2 {
/// ==================== IEIP712 ====================
function DOMAIN_SEPARATOR() external view returns (bytes32);
/// ==================== IAllowanceTransfer ====================
/// @notice The permit data for a token
struct PermitDetails {
// ERC20 token address
address token;
// the maximum amount allowed to spend
uint160 amount;
// timestamp at which a spender's token allowances become invalid
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice The permit message signed for a single token allowance
struct PermitSingle {
// the permit data for a single token alownce
PermitDetails details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The permit message signed for multiple token allowances
struct PermitBatch {
// the permit data for multiple token allowances
PermitDetails[] details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The saved permissions
/// @dev This info is saved per owner, per token, per spender and all signed over in the permit message
/// @dev Setting amount to type(uint160).max sets an unlimited approval
struct PackedAllowance {
// amount allowed
uint160 amount;
// permission expiry
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitSingle Data signed over by the owner specifying the terms of approval
/// @param signature The owner's signature over the permit data
function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
/// @notice Transfer approved tokens from one address to another
/// @param from The address to transfer from
/// @param to The address of the recipient
/// @param amount The amount of the token to transfer
/// @param token The token address to transfer
/// @dev Requires the from address to have approved at least the desired amount
/// of tokens to msg.sender.
function transferFrom(address from, address to, uint160 amount, address token) external;
/// @notice Approves the spender to use up to amount of the specified token up until the expiration
/// @param token The token to approve
/// @param spender The spender address to approve
/// @param amount The approved amount of the token
/// @param expiration The timestamp at which the approval is no longer valid
/// @dev The packed allowance also holds a nonce, which will stay unchanged in approve
/// @dev Setting amount to type(uint160).max sets an unlimited approval
function approve(address token, address spender, uint160 amount, uint48 expiration) external;
/// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.
/// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]
/// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.
function allowance(address user, address token, address spender)
external
view
returns (PackedAllowance memory);
/// ==================== ISignatureTransfer ====================
/// @notice The token and amount details for a transfer signed in the permit transfer signature
struct TokenPermissions {
// ERC20 token address
address token;
// the maximum amount that can be spent
uint256 amount;
}
/// @notice The signed permit message for a single token transfer
struct PermitTransferFrom {
TokenPermissions permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/// @notice Used to reconstruct the signed permit message for multiple token transfers
/// @dev Do not need to pass in spender address as it is required that it is msg.sender
/// @dev Note that a user still signs over a spender address
struct PermitBatchTransferFrom {
// the tokens and corresponding amounts permitted for a transfer
TokenPermissions[] permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/// @notice Specifies the recipient address and amount for batched transfers.
/// @dev Recipients and amounts correspond to the index of the signed token permissions array.
/// @dev Reverts if the requested amount is greater than the permitted signed amount.
struct SignatureTransferDetails {
// recipient address
address to;
// spender requested amount
uint256 requestedAmount;
}
/// @notice Transfers a token using a signed permit message
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param signature The signature to verify
function permitTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes calldata signature
) external;
/// @notice Transfers multiple tokens using a signed permit message
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails Specifies the recipient and requested amount for the token transfer
/// @param signature The signature to verify
function permitTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes calldata signature
) external;
/// @notice Transfers a token using a signed permit message
/// @notice Includes extra data provided by the caller to verify signature over
/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param witness Extra data to include when checking the user signature
/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
/// @param signature The signature to verify
function permitWitnessTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;
}
// contracts/8/libraries/PausableModuleV2.sol
/**
* @title PausableModuleV2 - Lightweight pause control module
* @notice Abstract contract providing pause functionality with Owner and Reporter roles
* @dev This is a gas-optimized implementation with zero external dependencies
* Storage optimization: address owner and uint64 pauseUntil are packed into the same slot
*/
abstract contract PausableModuleV2 {
// ============ Constant Variables ============
/// @notice Zero address constant used for address validation
address private constant ZERO_ADDRESS = address(0);
/// @notice Value indicating contract is not paused
/// @dev Using 1 instead of 0 for gas optimization (avoids expensive zero->non-zero SSTORE)
uint64 private constant NOT_PAUSED = 1;
/// @notice Value indicating contract is paused by owner
uint64 private constant OWNER_PAUSED = type(uint64).max;
/// @notice Reporter pause duration (24 hours)
uint256 private constant REPORTER_PAUSE_DURATION = 24 hours;
// ============ Mutable State Variables ============
/// @notice Contract owner address
/// @dev Packed with pauseUntil in the same storage slot
address public owner;
/// @notice Pause state timestamp
/// @dev NOT_PAUSED (1) = not paused, type(uint64).max = owner paused, timestamp = reporter paused
/// @dev Packed with owner in the same storage slot
uint64 public pauseUntil = NOT_PAUSED;
/// @notice Mapping of reporter addresses
mapping(address => bool) public reporters;
// ============ Custom Errors ============
/// @notice Thrown when contract is already paused
error AlreadyPaused();
/// @notice Thrown when contract is already paused by owner
/// @dev This error is thrown when owner tries to pause an already owner-paused contract
error AlreadyPausedByOwner();
/// @notice Thrown when reporter state is already set
error AlreadySet();
/// @notice Thrown when caller is not the owner
error NotOwner();
/// @notice Thrown when caller is not a reporter
error NotReporter();
/// @notice Thrown when contract is not paused
error NotPaused();
/// @notice Thrown when zero address is provided
error ZeroAddress();
// ============ Events ============
/// @notice Emitted when ownership is transferred
/// @param previousOwner Previous owner address
/// @param newOwner New owner address
event OwnerChanged(address indexed previousOwner, address indexed newOwner);
/// @notice Emitted when a reporter is added or removed
/// @param reporter Reporter address
/// @param added True if added, false if removed
event ReporterUpdated(address indexed reporter, bool added);
/// @notice Emitted when contract is paused by owner
event OwnerPaused();
/// @notice Emitted when contract is unpaused by owner
event OwnerUnpaused();
/// @notice Emitted when contract is paused by reporter
/// @param reporter Reporter address that triggered the pause
/// @param until Timestamp when pause will automatically expire
event ReporterPaused(address indexed reporter, uint64 until);
// ============ Modifiers ============
/// @notice Modifier to ensure contract is not paused
modifier whenNotPaused() {
if (block.timestamp < pauseUntil) revert AlreadyPaused();
_;
}
/// @notice Modifier to restrict access to owner only
modifier onlyOwner() {
if (msg.sender != owner) revert NotOwner();
_;
}
/// @notice Modifier to restrict access to reporters only
modifier onlyReporter() {
if (!reporters[msg.sender]) revert NotReporter();
_;
}
// ============ Constructor ============
/// @notice Constructor to initialize the contract with an owner
/// @param _owner Initial owner address
constructor(address _owner) {
if (_owner == ZERO_ADDRESS) revert ZeroAddress();
owner = _owner;
}
// ============ Owner Functions ============
/// @notice Transfer ownership to a new address
/// @dev Only callable by the current owner
/// @dev Emits OwnerChanged event with previousOwner (current owner) and newOwner
/// @param newOwner New owner address
function transferOwnership(address newOwner) external onlyOwner {
if (newOwner == ZERO_ADDRESS) revert ZeroAddress();
address previousOwner = owner;
owner = newOwner;
emit OwnerChanged(previousOwner, newOwner);
}
/// @notice Add or remove a reporter
/// @dev Only callable by the owner
/// @param reporter Reporter address to add or remove
/// @param added True to add, false to remove
function setReporter(address reporter, bool added) external onlyOwner {
if (reporter == ZERO_ADDRESS) revert ZeroAddress();
if (reporters[reporter] == added) revert AlreadySet();
reporters[reporter] = added;
emit ReporterUpdated(reporter, added);
}
/// @notice Pause the contract (until manually unpaused by owner)
/// @dev Only callable by the owner
/// @dev Sets pauseUntil to OWNER_PAUSED (type(uint64).max) to indicate owner pause
/// @dev Reverts if contract is already paused by owner
/// @dev Owner can override reporter pause (temporary pause) by calling this function
function pause() external onlyOwner {
if(pauseUntil == OWNER_PAUSED) revert AlreadyPausedByOwner();
pauseUntil = OWNER_PAUSED;
emit OwnerPaused();
}
/// @notice Unpause the contract
/// @dev Only callable by the owner
/// @dev Resets pauseUntil to NOT_PAUSED, clearing both owner and reporter pauses
/// @dev Reverts if contract is not paused
/// @dev Gas optimized: using NOT_PAUSED (1) avoids expensive zero->non-zero SSTORE on next pause
function unpause() external onlyOwner {
if(!isPaused()) revert NotPaused();
pauseUntil = NOT_PAUSED;
emit OwnerUnpaused();
}
// ============ Reporter Functions ============
/// @notice Pause the contract for 24 hours
/// @dev Only callable by a reporter
/// @dev Reverts if contract is already paused
/// @dev Sets pauseUntil to block.timestamp + 24 hours
function pauseByReporter() external onlyReporter whenNotPaused {
pauseUntil = uint64(block.timestamp + REPORTER_PAUSE_DURATION);
emit ReporterPaused(msg.sender, pauseUntil);
}
// ============ View Functions ============
/// @notice Check if the contract is currently paused
/// @return True if paused, false otherwise
/// @dev Returns true if block.timestamp < pauseUntil
function isPaused() public view returns (bool) {
return block.timestamp < pauseUntil;
}
}
// node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
// contracts/8/libraries/RevertReasonForwarder.sol
library RevertReasonForwarder {
function reRevert() internal pure {
// bubble up revert reason from latest external call
/// @solidity memory-safe-assembly
assembly { // solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
// contracts/8/libraries/SafeMath.sol
library SafeMath {
uint256 constant WAD = 10**18;
uint256 constant RAY = 10**27;
function wad() public pure returns (uint256) {
return WAD;
}
function ray() public pure returns (uint256) {
return RAY;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a <= b ? a : b;
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
function sqrt(uint256 a) internal pure returns (uint256 b) {
if (a > 3) {
b = a;
uint256 x = a / 2 + 1;
while (x < b) {
b = x;
x = (a / x + x) / 2;
}
} else if (a != 0) {
b = 1;
}
}
function wmul(uint256 a, uint256 b) internal pure returns (uint256) {
return mul(a, b) / WAD;
}
function wmulRound(uint256 a, uint256 b) internal pure returns (uint256) {
return add(mul(a, b), WAD / 2) / WAD;
}
function rmul(uint256 a, uint256 b) internal pure returns (uint256) {
return mul(a, b) / RAY;
}
function rmulRound(uint256 a, uint256 b) internal pure returns (uint256) {
return add(mul(a, b), RAY / 2) / RAY;
}
function wdiv(uint256 a, uint256 b) internal pure returns (uint256) {
return div(mul(a, WAD), b);
}
function wdivRound(uint256 a, uint256 b) internal pure returns (uint256) {
return add(mul(a, WAD), b / 2) / b;
}
function rdiv(uint256 a, uint256 b) internal pure returns (uint256) {
return div(mul(a, RAY), b);
}
function rdivRound(uint256 a, uint256 b) internal pure returns (uint256) {
return add(mul(a, RAY), b / 2) / b;
}
function wpow(uint256 x, uint256 n) internal pure returns (uint256) {
uint256 result = WAD;
while (n > 0) {
if (n % 2 != 0) {
result = wmul(result, x);
}
x = wmul(x, x);
n /= 2;
}
return result;
}
function rpow(uint256 x, uint256 n) internal pure returns (uint256) {
uint256 result = RAY;
while (n > 0) {
if (n % 2 != 0) {
result = rmul(result, x);
}
x = rmul(x, x);
n /= 2;
}
return result;
}
function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 quotient = div(a, b);
uint256 remainder = a - quotient * b;
if (remainder > 0) {
return quotient + 1;
} else {
return quotient;
}
}
}
// contracts/8/interfaces/IDexRouter.sol
interface IDexRouter {
/// =============================================================
/// ======================= Structs =============================
/// =============================================================
struct BaseRequest {
uint256 fromToken;
address toToken;
uint256 fromTokenAmount;
uint256 minReturnAmount;
uint256 deadLine;
}
struct RouterPath {
address[] mixAdapters;
address[] assetTo;
uint256[] rawData;
bytes[] extraData;
uint256 fromToken;
}
struct CommissionInfo {
bool isFromTokenCommission; //0x00
bool isToTokenCommission; //0x20
uint256 tokenWithMode; // 0x40, if isToTokenCommission, tokenWithMode is only the token address, otherwise tokenWithMode is the token address with mode encoded in high bits
uint256 toBCommission; // 0x60, 0 for no encoded commission data, 1 for no-toB commission, 2 for toB commission
uint256 commissionLength; // 0x80
uint256 commissionRate; // 0xa0
address referrerAddress; // 0xc0
uint256 commissionRate2; // 0xe0
address referrerAddress2; // 0x100
uint256 commissionRate3; // 0x120
address referrerAddress3; // 0x140
uint256 commissionRate4; // 0x160
address referrerAddress4; // 0x180
uint256 commissionRate5; // 0x1a0
address referrerAddress5; // 0x1c0
uint256 commissionRate6; // 0x1e0
address referrerAddress6; // 0x200
uint256 commissionRate7; // 0x220
address referrerAddress7; // 0x240
uint256 commissionRate8; // 0x260
address referrerAddress8; // 0x280
}
struct TrimInfo {
bool hasTrim; // 0x00
uint256 trimRate; // 0x20
address trimAddress; // 0x40
uint256 toBTrim; // 0x60, 0 for no encoded trim data, 1 for no-toB trim, 2 for toB trim
uint256 expectAmountOut; // 0x80
uint256 chargeRate; // 0xa0
address chargeAddress; // 0xc0
}
struct Permit2Info {
address owner; // 0x00
uint256 nonce; // 0x20
uint256 deadline; // 0x40
bytes signature; // 0x60
uint256[] amounts; // 0x80
}
struct ExtraData {
CommissionInfo commissionInfo;
TrimInfo trimInfo;
Permit2Info permit2Info;
address refundTo;
}
struct SwapCache {
address payer;
address refundTo;
address receiver;
address toToken;
}
struct AfterSwapParams {
CommissionInfo commissionInfo;
uint256 consumeAmount;
uint256 targetTokenBefore;
address srcToken;
address toToken;
address receiver;
address payer;
uint256 mode;
uint256 fromTokenCommissionAmount;
}
struct ExactOutSwapCache {
uint256 amountOut;
uint256 maxConsume;
address payer;
}
}
// contracts/8/libraries/SafeERC20.sol
// File @1inch/solidity-utils/contracts/libraries/SafeERC20.sol@v2.1.1
library SafeERC20 {
error SafeTransferFailed();
error SafeTransferFromFailed();
error ForceApproveFailed();
error SafeIncreaseAllowanceFailed();
error SafeDecreaseAllowanceFailed();
error SafePermitBadLength();
// Ensures method do not revert or return boolean `true`, admits call to non-smart-contract
function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
bytes4 selector = token.transferFrom.selector;
bool success;
/// @solidity memory-safe-assembly
assembly { // solhint-disable-line no-inline-assembly
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), from)
mstore(add(data, 0x24), to)
mstore(add(data, 0x44), amount)
success := call(gas(), token, 0, data, 100, 0x0, 0x20)
if success {
switch returndatasize()
case 0 { success := gt(extcodesize(token), 0) }
default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
}
}
if (!success) revert SafeTransferFromFailed();
}
// Ensures method do not revert or return boolean `true`, admits call to non-smart-contract
function safeTransfer(IERC20 token, address to, uint256 value) internal {
if (!_makeCall(token, token.transfer.selector, to, value)) {
revert SafeTransferFailed();
}
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
forceApprove(token, spender, value);
}
// If `approve(from, to, amount)` fails, try to `approve(from, to, 0)` before retry
function forceApprove(IERC20 token, address spender, uint256 value) internal {
if (!_makeCall(token, token.approve.selector, spender, value)) {
if (!_makeCall(token, token.approve.selector, spender, 0) ||
!_makeCall(token, token.approve.selector, spender, value))
{
revert ForceApproveFailed();
}
}
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 allowance = token.allowance(address(this), spender);
if (value > type(uint256).max - allowance) revert SafeIncreaseAllowanceFailed();
forceApprove(token, spender, allowance + value);
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 allowance = token.allowance(address(this), spender);
if (value > allowance) revert SafeDecreaseAllowanceFailed();
forceApprove(token, spender, allowance - value);
}
function safePermit(IERC20 token, bytes calldata permit) internal {
bool success;
if (permit.length == 32 * 7) {
success = _makeCalldataCall(token, IERC20Permit.permit.selector, permit);
} else if (permit.length == 32 * 8) {
success = _makeCalldataCall(token, IDaiLikePermit.permit.selector, permit);
} else {
revert SafePermitBadLength();
}
if (!success) RevertReasonForwarder.reRevert();
}
function _makeCall(IERC20 token, bytes4 selector, address to, uint256 amount) private returns(bool success) {
/// @solidity memory-safe-assembly
assembly { // solhint-disable-line no-inline-assembly
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), to)
mstore(add(data, 0x24), amount)
success := call(gas(), token, 0, data, 0x44, 0x0, 0x20)
if success {
switch returndatasize()
case 0 { success := gt(extcodesize(token), 0) }
default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
}
}
}
function _makeCalldataCall(IERC20 token, bytes4 selector, bytes calldata args) private returns(bool success) {
/// @solidity memory-safe-assembly
assembly { // solhint-disable-line no-inline-assembly
let len := add(4, args.length)
let data := mload(0x40)
mstore(data, selector)
calldatacopy(add(data, 0x04), args.offset, args.length)
success := call(gas(), token, 0, data, len, 0x0, 0x20)
if success {
switch returndatasize()
case 0 { success := gt(extcodesize(token), 0) }
default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) }
}
}
}
}
// contracts/8/libraries/CommonLib.sol
library CommonLib {
function getBalanceOf(
address token,
address user
) internal view returns (uint256 amount) {
assembly {
function _revertWithReason(m, len) {
mstore(
0,
0x08c379a000000000000000000000000000000000000000000000000000000000
)
mstore(
0x20,
0x0000002000000000000000000000000000000000000000000000000000000000
)
mstore(0x40, m)
revert(0, len)
}
switch eq(token, _ETH)
case 1 {
amount := balance(user)
}
default {
let freePtr := mload(0x40)
mstore(0x40, add(freePtr, 0x24))
mstore(
freePtr,
0x70a0823100000000000000000000000000000000000000000000000000000000
) //balanceOf
mstore(add(freePtr, 0x04), user)
let success := staticcall(gas(), token, freePtr, 0x24, 0, 0x20)
if eq(success, 0) {
_revertWithReason(
0x00000015206765742062616c616e63654f66206661696c656400000000000000,
0x59
) // "get balanceOf failed"
}
amount := mload(0x00)
}
}
}
function validateDeadline(uint256 deadLine) internal view {
require(deadLine >= block.timestamp, "Route: expired");
}
function exeAdapter(
bool reverse,
address adapter,
address to,
address poolAddress,
bytes memory moreinfo,
address refundTo
) internal {
if (reverse) {
(bool s, bytes memory res) = address(adapter).call(
abi.encodePacked(
abi.encodeWithSelector(
IAdapter.sellQuote.selector,
to,
poolAddress,
moreinfo
),
ORIGIN_PAYER + uint(uint160(refundTo))
)
);
if (!s) {
_revert(res);
}
} else {
(bool s, bytes memory res) = address(adapter).call(
abi.encodePacked(
abi.encodeWithSelector(
IAdapter.sellBase.selector,
to,
poolAddress,
moreinfo
),
ORIGIN_PAYER + uint(uint160(refundTo))
)
);
if (!s) {
_revert(res);
}
}
}
/// @notice Converts a uint256 value into an address.
/// @param param The uint256 value to be converted.
/// @return result The address obtained from the conversion.
/// @dev This function is used to extract an address from a uint256,
/// typically used when dealing with low-level data operations or when addresses are packed into larger data types.
function bytes32ToAddress(
uint256 param
) internal pure returns (address result) {
assembly {
result := and(param, _ADDRESS_MASK)
}
}
// ================================ Private Functions ================================
/**
* @dev Reverts with returndata if present. Otherwise reverts with "FailedCall".
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/c64a1edb67b6e3f4a15cca8909c9482ad33a02b0/contracts/utils/Address.sol#L135-L149
*/
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
assembly ("memory-safe") {
revert(add(returndata, 0x20), mload(returndata))
}
} else {
revert("adaptor call failed");
}
}
}
// contracts/8/router/SwapWrapRouter.sol
library SwapWrapRouter {
// ================================================================================
// ============================== Public Functions ================================
// ================================================================================
/// @notice Executes a simple swap between ETH and WETH using encoded parameters.
/// @param orderId Unique identifier for the swap order, facilitating tracking and reference.
/// @param rawdata Encoded data containing swap direction, transfer mode and amount information using bit masks.
/// @dev This function supports bidirectional swaps between ETH and WETH with minimal gas overhead.
/// The rawdata parameter encodes:
/// - Transfer mode in bits [251:249]: LEGACY(0b000), PERMIT2_ALLOWANCE(0b001), PERMIT2_SIGNATURE(0b011), DIRECT(0b101)
/// - Direction (reversed flag) in bit 255: false=ETH->WETH, true=WETH->ETH
function swapWrap(uint256 orderId, uint256 rawdata, IDexRouter.ExtraData memory extraData) public {
bool reversed;
uint128 amount;
uint256 mode;
assembly {
reversed := and(rawdata, _REVERSE_MASK)
amount := and(rawdata, SWAP_AMOUNT)
mode := and(rawdata, _TRANSFER_MODE_MASK)
}
}
/// @notice Executes a swap between ETH and WETH using structured base request parameters to a specified receiver.
/// @param orderId Unique identifier for the swap order, facilitating tracking and reference.
/// @param receiver The address that will receive the swapped tokens.
/// @param baseRequest Struct containing essential swap parameters including source token (with mode in high bits [251:249]), destination token, amount, minimum return, and deadline.
/// @dev This function validates that the token pair is either ETH->WETH or WETH->ETH and executes the swap accordingly.
/// It extracts the amount and mode from the baseRequest and determines the swap direction based on the token addresses.
function swapWrapToWithBaseRequest(
uint256 orderId,
address receiver,
IDexRouter.BaseRequest calldata baseRequest,
IDexRouter.ExtraData memory extraData
)
public
{
CommonLib.validateDeadline(baseRequest.deadLine);
bool reversed;
address fromTokenAddr = CommonLib.bytes32ToAddress(baseRequest.fromToken);
if (fromTokenAddr == _ETH && baseRequest.toToken == _WETH) {
reversed = false;
} else if (fromTokenAddr == _WETH && baseRequest.toToken == _ETH) {
reversed = true;
} else {
revert("SwapWrap: invalid token pair");
}
}
}
// contracts/8/DexRouter.sol
contract DexRouter is IDexRouter, PausableModuleV2, ReentrancyGuard {
constructor(address _owner) PausableModuleV2(_owner) ReentrancyGuard() {}
/// ================================ swapWrap related ================================
function swapWrap(uint256 orderId, uint256 rawdata) external payable nonReentrant whenNotPaused {
IDexRouter.ExtraData memory extraData;
return SwapWrapRouter.swapWrap(orderId, rawdata, extraData);
}
function swapWrapToWithBaseRequest(
uint256 orderId,
address receiver,
BaseRequest calldata baseRequest
)
external
payable
nonReentrant
whenNotPaused
{
IDexRouter.ExtraData memory extraData;
return SwapWrapRouter.swapWrapToWithBaseRequest(orderId, receiver, baseRequest, extraData);
}
}Constructor Arguments0xB2c945372e33a8BF4629370eEB3B86e06539C17b Hardhat Verify Plugin VersionNo response Repo Link (Optional)No response Additional DetailsI am trying to write a contract (DexRouter) with external library. I build it with solc 0.8.17 and deployed it with foundry tool. I want to keep to use solc 0.8.17 and deploy it with foundry tool, I think it's easier. So I want to know how can I verify it if https://era.zksync.network is closed? Will era.zksync.network support etherscan api v2? |
Replies: 1 comment 2 replies
|
Hi @alphafitz11! Instead of using Etherscan's block explorer which is soon to be discontinued, you can instead verify your contract over at the official ZKsync Era block explorer (https://explorer.zksync.io/). You can use the Web UI or the API. We also recommend you use hardhat-zksync-verify plugin if you develop with hardhat, or foundry-zksync if you develop with foundry, which you can also use to verify your contracts. I'll mark this as answered for now, feel free to unmark if you have further questions. |
Hi @alphafitz11!
Instead of using Etherscan's block explorer which is soon to be discontinued, you can instead verify your contract over at the official ZKsync Era block explorer (https://explorer.zksync.io/).
You can use the Web UI or the API.
We also recommend you use hardhat-zksync-verify plugin if you develop with hardhat, or foundry-zksync if you develop with foundry, which you can also use to verify your contracts.
I'll mark this as answered for now, feel free to unmark if you have further questions.