Feature Tip: Add private address tag to any address under My Name Tag !
Source Code
Latest 25 from a total of 1,775 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Withdraw | 24565451 | 3 hrs ago | IN | 0 ETH | 0.00000681 | ||||
| Withdraw | 24564338 | 6 hrs ago | IN | 0 ETH | 0.0000135 | ||||
| Deposit | 24564227 | 7 hrs ago | IN | 0.00625 ETH | 0.00005983 | ||||
| Withdraw | 24563975 | 8 hrs ago | IN | 0 ETH | 0.00001163 | ||||
| Withdraw | 24563941 | 8 hrs ago | IN | 0 ETH | 0.00001367 | ||||
| Deposit | 24563428 | 10 hrs ago | IN | 101 ETH | 0.00000318 | ||||
| Withdraw | 24562722 | 12 hrs ago | IN | 0 ETH | 0.00000694 | ||||
| Deposit | 24562638 | 12 hrs ago | IN | 165 ETH | 0.00000214 | ||||
| Withdraw | 24562616 | 12 hrs ago | IN | 0 ETH | 0.00000695 | ||||
| Withdraw | 24562435 | 13 hrs ago | IN | 0 ETH | 0.00000405 | ||||
| Withdraw | 24562240 | 14 hrs ago | IN | 0 ETH | 0.00000527 | ||||
| Withdraw | 24562193 | 14 hrs ago | IN | 0 ETH | 0.00000525 | ||||
| Withdraw | 24562155 | 14 hrs ago | IN | 0 ETH | 0.00000434 | ||||
| Withdraw | 24562092 | 14 hrs ago | IN | 0 ETH | 0.00000466 | ||||
| Withdraw | 24562041 | 14 hrs ago | IN | 0 ETH | 0.00000459 | ||||
| Withdraw | 24562020 | 14 hrs ago | IN | 0 ETH | 0.00000411 | ||||
| Withdraw | 24561999 | 14 hrs ago | IN | 0 ETH | 0.00000436 | ||||
| Withdraw | 24561988 | 14 hrs ago | IN | 0 ETH | 0.00000477 | ||||
| Deposit | 24561877 | 15 hrs ago | IN | 1 ETH | 0.00005864 | ||||
| Deposit | 24561869 | 15 hrs ago | IN | 0 ETH | 0.00011491 | ||||
| Withdraw | 24559824 | 22 hrs ago | IN | 0 ETH | 0.00000436 | ||||
| Deposit | 24559794 | 22 hrs ago | IN | 608.3 ETH | 0.00005908 | ||||
| Deposit | 24559747 | 22 hrs ago | IN | 0 ETH | 0.00000264 | ||||
| Withdraw | 24559730 | 22 hrs ago | IN | 0 ETH | 0.00000645 | ||||
| Deposit | 24558482 | 26 hrs ago | IN | 13.9 ETH | 0.00000577 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Deposit | 24566160 | 55 mins ago | 0.01956027 ETH | ||||
| Deposit | 24565773 | 2 hrs ago | 0.12 ETH | ||||
| Transfer | 24565451 | 3 hrs ago | 4.89985 ETH | ||||
| Transfer | 24564338 | 6 hrs ago | 19.99985 ETH | ||||
| Transfer | 24563975 | 8 hrs ago | 2.99985 ETH | ||||
| Transfer | 24563941 | 8 hrs ago | 0.23176155 ETH | ||||
| Deposit | 24563507 | 9 hrs ago | 31.8505 ETH | ||||
| Deposit | 24562916 | 11 hrs ago | 0.15355099 ETH | ||||
| Deposit | 24562723 | 12 hrs ago | 0.009869 ETH | ||||
| Transfer | 24562722 | 12 hrs ago | 0.009869 ETH | ||||
| Deposit | 24562697 | 12 hrs ago | 0.01001991 ETH | ||||
| Transfer | 24562616 | 12 hrs ago | 165.18985 ETH | ||||
| Transfer | 24562435 | 13 hrs ago | 17.697715 ETH | ||||
| Deposit | 24562388 | 13 hrs ago | 17.69786566 ETH | ||||
| Transfer | 24562240 | 14 hrs ago | 0.69985 ETH | ||||
| Transfer | 24562155 | 14 hrs ago | 0.30203126 ETH | ||||
| Deposit | 24562094 | 14 hrs ago | 3.880964 ETH | ||||
| Transfer | 24562092 | 14 hrs ago | 3.880964 ETH | ||||
| Deposit | 24562046 | 14 hrs ago | 0.0050206 ETH | ||||
| Deposit | 24562042 | 14 hrs ago | 2.148265 ETH | ||||
| Transfer | 24562041 | 14 hrs ago | 2.148265 ETH | ||||
| Deposit | 24562033 | 14 hrs ago | 0.0629302 ETH | ||||
| Deposit | 24562023 | 14 hrs ago | 1.728133 ETH | ||||
| Transfer | 24562020 | 14 hrs ago | 1.728133 ETH | ||||
| Deposit | 24562010 | 14 hrs ago | 2.14841489 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
SpotVault
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {IPerpVault} from "../interfaces/IPerpVault.sol";
interface IArbSys {
function withdrawEth(address destination) external payable returns (uint256);
}
interface IL2Gateway {
function outboundTransfer(address _l1Token, address _to, uint256 _amount, bytes calldata _data)
external
returns (bytes memory);
}
/// @title SpotVault
/// @notice Two-of-two multisignature vault supporting ETH and ERC20 deposits with optional permit flows.
/// @dev Signer set can be updated by the owner; withdrawals require unanimous approval from both signers.
contract SpotVault is ReentrancyGuard, Ownable, Pausable {
using SafeERC20 for IERC20;
uint256 private constant SIGNER_COUNT = 2;
uint256 private constant SIGNATURE_THRESHOLD = 2;
address private constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
IArbSys private immutable ARBSYS;
IL2Gateway private immutable L2_GATEWAY;
/// @notice Emitted when a user deposits funds into the vault.
event Deposit(address indexed from, address indexed token, uint256 amount, uint256 accountId);
/// @notice Emitted when a user deposits funds into the vault by permit.
event DepositWithPermit(address indexed from, address indexed token, uint256 amount);
/// @notice Emitted when a withdrawal is executed.
event Withdraw(
uint256 indexed clientWithdrawId,
address indexed token,
address indexed to,
uint256 amount,
bool isWithdrawToArb
);
/// @notice Emitted when funds are transferred to PerpVault.
event TransferToPerp(
uint256 indexed clientWithdrawId,
address indexed token,
address indexed perpVault,
uint256 amount,
uint256 toAccountId,
uint256 starkKey
);
/// @notice Emitted whenever the authorized signer set changes.
event SignersConfigured(address indexed signer0, address indexed signer1);
/// @notice Emitted when the withdraw-to-Arb switch is toggled.
event WithdrawToArbStatusUpdated(bool enabled);
/// @notice Emitted when the owner rescues funds.
event EmergencyWithdraw(address indexed token, address indexed to, uint256 amount);
/// @notice Emitted when a cross-chain token mapping is added or updated.
event CrossChainTokenAddressAdded(address indexed edgeToken, address indexed arbToken);
/// @notice Emitted when the PerpVault destination is updated.
event PerpVaultUpdated(address indexed previousPerpVault, address indexed newPerpVault);
/// @dev Error definitions provide precise failure reasons for wallet operations.
error InvalidSignerCount(uint256 provided, uint256 expected);
error ZeroSignerAddress(uint256 index);
error DuplicateSigner(address signer);
error UnexpectedNativeValue(uint256 provided);
error InvalidNativeAmount(uint256 expected, uint256 provided);
error SignatureLengthMismatch(uint256 signers, uint256 signatures);
error InsufficientSigners(uint256 provided, uint256 required);
error TransactionExpired(uint256 expiration, uint256 current);
error SignerMismatch(uint256 index, address expected, address actual);
error SignerNotAuthorized(address signer);
error OrderAlreadyExecuted(bytes32 withdrawalHash);
error NativeTransferFailed(address to, uint256 amount);
error NativeTokenNotSupported();
error ZeroPerpVault();
error UnsupportedToken(address token);
error InvalidUserSignature(address expected, address actual);
error WithdrawToArbDisabled();
error ZeroOwner();
error ZeroRecipient();
error InsufficientBalance(uint256 available, uint256 requested);
error ZeroTokenAddress();
error ZeroCrossChainContract();
error CrossChainTokenAddressMissing(address edgeToken);
error WithdrawToArbNotEnabled();
error TransferAmountMismatch(uint256 expected, uint256 actual);
struct WithdrawalOrder {
address to;
uint256 amount;
address token;
bool executed;
}
/// @notice Current list of authorized signers.
address[] public signers;
/// @dev Tracks whether an address is an authorized signer for constant-time lookups.
mapping(address => bool) private signerStatus;
/// @dev Records processed withdrawal orders to prevent replay.
mapping(bytes32 => WithdrawalOrder) private orders;
/// @dev Token address mapping between EDGE and ARB
mapping(address edgeTokenAddress => address arbTokenAddress) private crossChainTokenAddressMap;
/// @notice Flag controlling whether withdraw to ARB are allowed.
bool public enableWithdrawToArb;
/// @notice Destination PerpVault used for cross-product transfers.
address public perpVault = address(0);
/// @notice Deploys the vault with an exact set of two unique signers.
/// @param allowedSigners The signer addresses that control the vault.
/// @param edgeUsdc Token address on Edge network to seed the cross-chain map.
/// @param arbUsdc Token address on Arbitrum network corresponding to `edgeUsdc`.
/// @param arbSys Address of the ArbSys precompile on the deployment network.
/// @param l2Gateway Address of the Arbitrum L2 gateway contract used for outbound transfers.
/// @param owner_ Address that will receive ownership of the vault controls.
constructor(
address[] memory allowedSigners,
address edgeUsdc,
address arbUsdc,
address arbSys,
address l2Gateway,
address owner_
) {
_setSigners(allowedSigners);
if (owner_ == address(0)) {
revert ZeroOwner();
}
if (edgeUsdc == address(0) || arbUsdc == address(0)) {
revert ZeroTokenAddress();
}
if (arbSys == address(0) || l2Gateway == address(0)) {
revert ZeroCrossChainContract();
}
ARBSYS = IArbSys(arbSys);
L2_GATEWAY = IL2Gateway(l2Gateway);
enableWithdrawToArb = true;
crossChainTokenAddressMap[edgeUsdc] = arbUsdc;
emit CrossChainTokenAddressAdded(edgeUsdc, arbUsdc);
_transferOwnership(owner_);
}
/// @notice Accepts plain ETH transfers.
receive() external payable {}
/// @notice Deposits ETH or ERC20 tokens into the vault.
/// @dev When depositing ETH, `token` must equal the sentinel native token address.
/// @param token Address of the asset to deposit (native sentinel for ETH).
/// @param amount Amount of tokens or wei to deposit.
/// @param accountId Off-chain account identifier emitted for indexing.
function deposit(address token, uint256 amount, uint256 accountId) external payable nonReentrant whenNotPaused {
if (_isNative(token)) {
if (msg.value != amount) {
revert InvalidNativeAmount(amount, msg.value);
}
} else {
if (msg.value != 0) {
revert UnexpectedNativeValue(msg.value);
}
IERC20 tokenContract = IERC20(token);
uint256 balanceBefore = tokenContract.balanceOf(address(this));
tokenContract.safeTransferFrom(msg.sender, address(this), amount);
uint256 received = tokenContract.balanceOf(address(this)) - balanceBefore;
if (received != amount) {
revert TransferAmountMismatch(amount, received);
}
}
emit Deposit(msg.sender, token, amount, accountId);
}
/// @notice Deposits ERC20 tokens using EIP-2612 permit authorization.
/// @param token Address of the ERC20 token supporting permit.
/// @param amount Amount of tokens to transfer.
/// @param owner Token owner granting approval via permit.
/// @param deadline Permit expiration timestamp.
/// @param v Signature v component.
/// @param r Signature r component.
/// @param s Signature s component.
function depositErc20WithPermit(
address token,
uint256 amount,
address owner,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external nonReentrant whenNotPaused {
if (_isNative(token)) {
revert NativeTokenNotSupported();
}
IERC20Permit(token).permit(owner, address(this), amount, deadline, v, r, s);
IERC20 tokenContract = IERC20(token);
uint256 balanceBefore = tokenContract.balanceOf(address(this));
tokenContract.safeTransferFrom(owner, address(this), amount);
uint256 received = tokenContract.balanceOf(address(this)) - balanceBefore;
if (received != amount) {
revert TransferAmountMismatch(amount, received);
}
emit DepositWithPermit(owner, token, amount);
}
/// @notice Executes a withdrawal after verifying the provided signatures.
/// @param to Recipient of the withdrawal.
/// @param amount Amount of tokens or wei to withdraw.
/// @param token Asset being withdrawn.
/// @param expireTime Absolute timestamp after which the withdrawal is invalid.
/// @param clientWithdrawId Unique order identifier preventing replay.
/// @param allSigners Signers corresponding to each provided signature.
/// @param signatures Signatures authorizing the withdrawal.
function withdraw(
address to,
uint256 amount,
address token,
uint256 expireTime,
uint256 clientWithdrawId,
bool isWithdrawToArb,
address[] memory allSigners,
bytes[] memory signatures
) external nonReentrant whenNotPaused {
if (to == address(0)) {
revert ZeroRecipient();
}
uint256 signerCount = allSigners.length;
if (signerCount < SIGNATURE_THRESHOLD) {
revert InsufficientSigners(signerCount, SIGNATURE_THRESHOLD);
}
if (signerCount != signatures.length) {
revert SignatureLengthMismatch(signerCount, signatures.length);
}
_ensureDistinctSigners(allSigners);
if (expireTime < block.timestamp) {
revert TransactionExpired(expireTime, block.timestamp);
}
bytes32 operationHash =
withdrawalSigHash(to, amount, token, expireTime, clientWithdrawId, block.chainid, isWithdrawToArb);
bytes32 ethSignedHash = ECDSA.toEthSignedMessageHash(operationHash);
for (uint256 index = 0; index < signerCount; ++index) {
address expectedSigner = allSigners[index];
address recoveredSigner = ECDSA.recover(ethSignedHash, signatures[index]);
if (recoveredSigner != expectedSigner) {
revert SignerMismatch(index, expectedSigner, recoveredSigner);
}
if (!signerStatus[recoveredSigner]) {
revert SignerNotAuthorized(recoveredSigner);
}
}
_recordOrder(operationHash, to, amount, token);
if (isWithdrawToArb) {
if (!enableWithdrawToArb) {
revert WithdrawToArbNotEnabled();
}
if (_isNative(token)) {
ARBSYS.withdrawEth{value: amount}(to);
} else {
address arbTokenAddress = crossChainTokenAddressMap[token];
if (arbTokenAddress == address(0)) {
revert CrossChainTokenAddressMissing(token);
}
L2_GATEWAY.outboundTransfer(arbTokenAddress, to, amount, "");
}
} else {
if (_isNative(token)) {
_transferNative(to, amount);
} else {
IERC20(token).safeTransfer(to, amount);
}
}
emit Withdraw(clientWithdrawId, token, to, amount, isWithdrawToArb);
}
/// @notice Transfers USDT from this SpotVault directly into a PerpVault position after signature checks.
/// @dev Prevents router frontrunning by binding destination position to the signed payload.
/// @param token Asset being transferred (must match PerpVault.USDT_ADDRESS()).
/// @param amount Amount of tokens to transfer.
/// @param expireTime Absolute timestamp after which the operation is invalid.
/// @param clientWithdrawId Unique identifier preventing replay.
/// @param toAccountId Target PerpVault position id.
/// @param starkKey Stark key of the destination Perp account.
/// @param allSigners Signers corresponding to each provided signature.
/// @param signatures Signatures authorizing the transfer.
function transferToPerp(
address token,
uint256 amount,
uint256 expireTime,
uint256 clientWithdrawId,
uint256 toAccountId,
uint256 starkKey,
address[] memory allSigners,
bytes[] memory signatures
) external nonReentrant whenNotPaused {
if (perpVault == address(0)) {
revert ZeroPerpVault();
}
if (!enableWithdrawToArb) {
revert WithdrawToArbDisabled();
}
uint256 signerCount = allSigners.length;
if (signerCount < SIGNATURE_THRESHOLD) {
revert InsufficientSigners(signerCount, SIGNATURE_THRESHOLD);
}
if (signerCount != signatures.length) {
revert SignatureLengthMismatch(signerCount, signatures.length);
}
_ensureDistinctSigners(allSigners);
if (expireTime < block.timestamp) {
revert TransactionExpired(expireTime, block.timestamp);
}
if (_isNative(token)) {
revert NativeTokenNotSupported();
}
address usdt = IPerpVault(perpVault).USDT_ADDRESS();
if (token != usdt) {
revert UnsupportedToken(token);
}
bytes32 operationHash =
transferToPerpSigHash(token, amount, expireTime, clientWithdrawId, toAccountId, starkKey, block.chainid);
bytes32 ethSignedHash = ECDSA.toEthSignedMessageHash(operationHash);
for (uint256 index = 0; index < signerCount; ++index) {
address expectedSigner = allSigners[index];
address recoveredSigner = ECDSA.recover(ethSignedHash, signatures[index]);
if (recoveredSigner != expectedSigner) {
revert SignerMismatch(index, expectedSigner, recoveredSigner);
}
if (!signerStatus[recoveredSigner]) {
revert SignerNotAuthorized(recoveredSigner);
}
}
_recordOrder(operationHash, perpVault, amount, token);
IERC20(token).safeApprove(perpVault, 0);
IERC20(token).safeApprove(perpVault, amount);
IPerpVault(perpVault).deposit(IERC20(token), amount, starkKey, toAccountId, "");
// Clear allowance to reduce residual approval surface area
IERC20(token).safeApprove(perpVault, 0);
emit TransferToPerp(clientWithdrawId, token, perpVault, amount, toAccountId, starkKey);
}
/// @notice Returns the hash that signers must authorize for a transfer to PerpVault.
function transferToPerpSigHash(
address token,
uint256 amount,
uint256 expireTime,
uint256 clientWithdrawId,
uint256 toAccountId,
uint256 starkKey,
uint256 chainId
) public view returns (bytes32) {
return keccak256(
abi.encodePacked(
"TRANSFER_TO_PERP",
perpVault,
token,
amount,
expireTime,
clientWithdrawId,
toAccountId,
starkKey,
address(this),
chainId
)
);
}
/// @notice Checks whether an address is part of the authorized signer set.
function isAllowedSigner(address signer) public view returns (bool) {
return signerStatus[signer];
}
/// @notice Returns the hash that signers must authorize for a withdrawal.
function withdrawalSigHash(
address to,
uint256 amount,
address token,
uint256 expireTime,
uint256 clientWithdrawId,
uint256 chainId,
bool isWithdrawToArb
) public view returns (bytes32) {
return keccak256(
abi.encodePacked(
"ERC20", to, amount, token, expireTime, clientWithdrawId, address(this), chainId, isWithdrawToArb
)
);
}
/// @notice Allows the owner to toggle the withdraw-to-Arb feature.
function setEnableWithdrawToArb(bool enabled) external onlyOwner {
enableWithdrawToArb = enabled;
emit WithdrawToArbStatusUpdated(enabled);
}
/// @notice Adds or updates a cross-chain token mapping.
/// @param edgeToken Token address on the Edge network.
/// @param arbToken Token address on Arbitrum.
function addCrossChainTokenAddress(address edgeToken, address arbToken) external onlyOwner {
if (edgeToken == address(0) || arbToken == address(0)) {
revert ZeroTokenAddress();
}
crossChainTokenAddressMap[edgeToken] = arbToken;
emit CrossChainTokenAddressAdded(edgeToken, arbToken);
}
/// @notice Allows the owner to update the destination PerpVault.
function setPerpVault(address newPerpVault) external onlyOwner {
_setPerpVault(newPerpVault);
}
/// @notice Allows the owner to rotate the signer set.
/// @param newSigners Replacement signer list; must contain exactly two distinct non-zero addresses.
function setSigners(address[] calldata newSigners) external onlyOwner {
_setSigners(newSigners);
}
/// @notice Allows the owner to pause deposits and withdrawals.
function pause() external onlyOwner {
_pause();
}
/// @notice Allows the owner to resume deposits and withdrawals.
function unpause() external onlyOwner {
_unpause();
}
/// @notice Allows the owner to rescue either native ETH or ERC20 tokens.
/// @param token Address of the asset to rescue (native sentinel for ETH).
/// @param amount Amount to withdraw; withdraws full balance when zero.
/// @param to Recipient of the rescued funds.
function emergencyWithdraw(address token, uint256 amount, address to) external onlyOwner nonReentrant {
if (to == address(0)) {
revert ZeroRecipient();
}
uint256 transferAmount = amount;
if (_isNative(token)) {
uint256 balance = address(this).balance;
if (transferAmount == 0) {
transferAmount = balance;
}
if (transferAmount > balance) {
revert InsufficientBalance(balance, transferAmount);
}
_transferNative(to, transferAmount);
} else {
uint256 balance = IERC20(token).balanceOf(address(this));
if (transferAmount == 0) {
transferAmount = balance;
}
if (transferAmount > balance) {
revert InsufficientBalance(balance, transferAmount);
}
IERC20(token).safeTransfer(to, transferAmount);
}
emit EmergencyWithdraw(token, to, transferAmount);
}
/// @dev Records a withdrawal order and reverts if the identifier was already used.
function _recordOrder(bytes32 orderHash, address to, uint256 amount, address token) private {
WithdrawalOrder storage order = orders[orderHash];
if (order.executed) {
revert OrderAlreadyExecuted(orderHash);
}
order.executed = true;
order.to = to;
order.amount = amount;
order.token = token;
}
/// @dev Checks whether the provided token denotes the configured native sentinel.
function _isNative(address token) private pure returns (bool) {
return token == NATIVE_TOKEN;
}
/// @dev Sends native ETH to the recipient and reverts if the transfer fails.
function _transferNative(address to, uint256 amount) private {
(bool success,) = to.call{value: amount}("");
if (!success) {
revert NativeTransferFailed(to, amount);
}
}
/// @dev Updates the PerpVault destination after validating the address.
function _setPerpVault(address newPerpVault) private {
if (newPerpVault == address(0)) {
revert ZeroPerpVault();
}
address previousPerpVault = perpVault;
perpVault = newPerpVault;
emit PerpVaultUpdated(previousPerpVault, newPerpVault);
}
/// @dev Updates the signer set and keeps the mapping in sync.
function _setSigners(address[] memory allowedSigners) private {
uint256 length = allowedSigners.length;
if (length != SIGNER_COUNT) {
revert InvalidSignerCount(length, SIGNER_COUNT);
}
_ensureDistinctSigners(allowedSigners);
for (uint256 i = 0; i < signers.length; ++i) {
signerStatus[signers[i]] = false;
}
delete signers;
for (uint256 i = 0; i < length; ++i) {
address signer = allowedSigners[i];
if (signer == address(0)) {
revert ZeroSignerAddress(i);
}
signers.push(signer);
signerStatus[signer] = true;
}
emit SignersConfigured(signers[0], signers[1]);
}
/// @dev Ensures that the provided addresses are distinct.
function _ensureDistinctSigners(address[] memory accounts) private pure {
uint256 length = accounts.length;
for (uint256 i = 0; i < length; ++i) {
address current = accounts[i];
for (uint256 j = i + 1; j < length; ++j) {
if (current == accounts[j]) {
revert DuplicateSigner(current);
}
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../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.
*
* By default, the owner account will be the one that deploys the contract. 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;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @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 {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @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 {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @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 amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` 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 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @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 v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../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 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.encodeWithSelector(token.transfer.selector, 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.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 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);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @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.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @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.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
/**
* @dev Returns an Ethereum Signed Data with intended validator, created from a
* `validator` and `data` according to the version 0 of EIP-191.
*
* See {recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title IPerpVault
/// @notice Interface for PerpVault contract
interface IPerpVault {
/// @notice Returns the USDT token address
function USDT_ADDRESS() external view returns (address);
/// @notice Make a deposit to the Starkware Layer2, after converting funds to USDT.
/// @param token The ERC20 token to convert from
/// @param amount The amount in Wei to deposit.
/// @param starkKey The starkKey of the L2 account to deposit into.
/// @param positionId The positionId of the L2 account to deposit into.
/// @param exchangeData Trade parameters for the exchange.
function deposit(IERC20 token, uint256 amount, uint256 starkKey, uint256 positionId, bytes calldata exchangeData)
external
payable
returns (uint256);
/// @notice Withdraw ERC20 from this wallet using 2 signers.
/// @param to the destination address to send an outgoing transaction
/// @param amount the amount in Wei to be sent
/// @param token the address of the erc20 token contract
/// @param expireTime the number of seconds since 1970 for which this transaction is valid
/// @param orderId the unique order id
/// @param allSigners all signer who sign the tx
/// @param signatures the signatures of tx
function withdrawErc20(
address to,
uint256 amount,
address token,
uint256 expireTime,
uint256 orderId,
address[] memory allSigners,
bytes[] memory signatures
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @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.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @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, it is bubbled up by this
* function (like regular Solidity function calls).
*
* 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.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @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`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}{
"remappings": [
"@solady/=lib/solady/src/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/=lib/openzeppelin-contracts/",
"../interfaces/=src/interfaces/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin/=lib/openzeppelin-contracts/contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address[]","name":"allowedSigners","type":"address[]"},{"internalType":"address","name":"edgeUsdc","type":"address"},{"internalType":"address","name":"arbUsdc","type":"address"},{"internalType":"address","name":"arbSys","type":"address"},{"internalType":"address","name":"l2Gateway","type":"address"},{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"edgeToken","type":"address"}],"name":"CrossChainTokenAddressMissing","type":"error"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"DuplicateSigner","type":"error"},{"inputs":[{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"requested","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"provided","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"InsufficientSigners","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"provided","type":"uint256"}],"name":"InvalidNativeAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"provided","type":"uint256"},{"internalType":"uint256","name":"expected","type":"uint256"}],"name":"InvalidSignerCount","type":"error"},{"inputs":[{"internalType":"address","name":"expected","type":"address"},{"internalType":"address","name":"actual","type":"address"}],"name":"InvalidUserSignature","type":"error"},{"inputs":[],"name":"NativeTokenNotSupported","type":"error"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"NativeTransferFailed","type":"error"},{"inputs":[{"internalType":"bytes32","name":"withdrawalHash","type":"bytes32"}],"name":"OrderAlreadyExecuted","type":"error"},{"inputs":[{"internalType":"uint256","name":"signers","type":"uint256"},{"internalType":"uint256","name":"signatures","type":"uint256"}],"name":"SignatureLengthMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"expected","type":"address"},{"internalType":"address","name":"actual","type":"address"}],"name":"SignerMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"SignerNotAuthorized","type":"error"},{"inputs":[{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"current","type":"uint256"}],"name":"TransactionExpired","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"TransferAmountMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"provided","type":"uint256"}],"name":"UnexpectedNativeValue","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"UnsupportedToken","type":"error"},{"inputs":[],"name":"WithdrawToArbDisabled","type":"error"},{"inputs":[],"name":"WithdrawToArbNotEnabled","type":"error"},{"inputs":[],"name":"ZeroCrossChainContract","type":"error"},{"inputs":[],"name":"ZeroOwner","type":"error"},{"inputs":[],"name":"ZeroPerpVault","type":"error"},{"inputs":[],"name":"ZeroRecipient","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"ZeroSignerAddress","type":"error"},{"inputs":[],"name":"ZeroTokenAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"edgeToken","type":"address"},{"indexed":true,"internalType":"address","name":"arbToken","type":"address"}],"name":"CrossChainTokenAddressAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountId","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DepositWithPermit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousPerpVault","type":"address"},{"indexed":true,"internalType":"address","name":"newPerpVault","type":"address"}],"name":"PerpVaultUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"signer0","type":"address"},{"indexed":true,"internalType":"address","name":"signer1","type":"address"}],"name":"SignersConfigured","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"clientWithdrawId","type":"uint256"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"perpVault","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAccountId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"starkKey","type":"uint256"}],"name":"TransferToPerp","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"clientWithdrawId","type":"uint256"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isWithdrawToArb","type":"bool"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"WithdrawToArbStatusUpdated","type":"event"},{"inputs":[{"internalType":"address","name":"edgeToken","type":"address"},{"internalType":"address","name":"arbToken","type":"address"}],"name":"addCrossChainTokenAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"accountId","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"depositErc20WithPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"enableWithdrawToArb","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"isAllowedSigner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"perpVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setEnableWithdrawToArb","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPerpVault","type":"address"}],"name":"setPerpVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"newSigners","type":"address[]"}],"name":"setSigners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"signers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expireTime","type":"uint256"},{"internalType":"uint256","name":"clientWithdrawId","type":"uint256"},{"internalType":"uint256","name":"toAccountId","type":"uint256"},{"internalType":"uint256","name":"starkKey","type":"uint256"},{"internalType":"address[]","name":"allSigners","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"}],"name":"transferToPerp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expireTime","type":"uint256"},{"internalType":"uint256","name":"clientWithdrawId","type":"uint256"},{"internalType":"uint256","name":"toAccountId","type":"uint256"},{"internalType":"uint256","name":"starkKey","type":"uint256"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"transferToPerpSigHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"expireTime","type":"uint256"},{"internalType":"uint256","name":"clientWithdrawId","type":"uint256"},{"internalType":"bool","name":"isWithdrawToArb","type":"bool"},{"internalType":"address[]","name":"allSigners","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"expireTime","type":"uint256"},{"internalType":"uint256","name":"clientWithdrawId","type":"uint256"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"bool","name":"isWithdrawToArb","type":"bool"}],"name":"withdrawalSigHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60c060405260068054610100600160a81b03191690553480156200002257600080fd5b50604051620031523803806200315283398101604081905262000045916200051f565b6001600055620000553362000193565b6001805460ff60a01b191690556200006d86620001e5565b6001600160a01b0381166200009557604051639905827b60e01b815260040160405180910390fd5b6001600160a01b0385161580620000b357506001600160a01b038416155b15620000d257604051636b093aad60e01b815260040160405180910390fd5b6001600160a01b0383161580620000f057506001600160a01b038216155b156200010f5760405163e371c81f60e01b815260040160405180910390fd5b6001600160a01b0380841660805282811660a0526006805460ff1916600117905585811660008181526005602052604080822080549489166001600160a01b031990951685179055517f06c89f9d10102f3c193eb0acecda53739a03fee0dac5b3c380bb161c53ffabe89190a3620001878162000193565b5050505050506200068c565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b805160028114620002185760405163377ca99960e21b815260048101829052600260248201526044015b60405180910390fd5b6200022382620003ee565b60005b6002548110156200028a57600060036000600284815481106200024d576200024d6200064e565b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff191691151591909117905560010162000226565b506200029960026000620004ae565b60005b8181101562000369576000838281518110620002bc57620002bc6200064e565b6020026020010151905060006001600160a01b0316816001600160a01b031603620002fe5760405163ed76175960e01b8152600481018390526024016200020f565b6002805460018181019092557f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0180546001600160a01b039093166001600160a01b031990931683179055600091825260036020526040909120805460ff191682179055016200029c565b5060026001815481106200038157620003816200064e565b6000918252602082200154600280546001600160a01b03909216929091620003ad57620003ad6200064e565b60009182526020822001546040516001600160a01b03909116917f028fc5ffa55a0b7bd9ee999be60b4ae77211c4381125e3a0cae1ba230645edca91a35050565b805160005b81811015620004a95760008382815181106200041357620004136200064e565b6020026020010151905060008260016200042e919062000664565b90505b838110156200049e578481815181106200044f576200044f6200064e565b60200260200101516001600160a01b0316826001600160a01b0316036200049557604051637010e27960e11b81526001600160a01b03831660048201526024016200020f565b60010162000431565b5050600101620003f3565b505050565b5080546000825590600052602060002090810190620004ce9190620004d1565b50565b5b80821115620004e85760008155600101620004d2565b5090565b634e487b7160e01b600052604160045260246000fd5b80516001600160a01b03811681146200051a57600080fd5b919050565b60008060008060008060c087890312156200053957600080fd5b86516001600160401b03808211156200055157600080fd5b818901915089601f8301126200056657600080fd5b81516020828211156200057d576200057d620004ec565b8160051b604051601f19603f83011681018181108682111715620005a557620005a5620004ec565b60405292835281830193508481018201928d841115620005c457600080fd5b948201945b83861015620005ed57620005dd8662000502565b85529482019493820193620005c9565b9a50620005fe90508b820162000502565b985050505050620006126040880162000502565b9350620006226060880162000502565b9250620006326080880162000502565b91506200064260a0880162000502565b90509295509295509295565b634e487b7160e01b600052603260045260246000fd5b808201808211156200068657634e487b7160e01b600052601160045260246000fd5b92915050565b60805160a051612aa0620006b26000396000611406015260006113110152612aa06000f3fe60806040526004361061012e5760003560e01c8063715018a6116100ab578063a37726621161006f578063a377266214610325578063b7947ef514610345578063cabb9e7a14610365578063dc2b6df01461039e578063f2fde38b146103be578063f3927487146103de57600080fd5b8063715018a61461029d5780638456cb59146102b257806385217809146102c757806388dfa7a6146102e75780638da5cb5b1461030757600080fd5b80634fe08484116100f25780634fe08484146101ef578063551512de14610219578063570828d4146102395780635c975abb146102595780636273010d1461027857600080fd5b80630efe6a8b1461013a5780631c2453a61461014f5780632079fb9a1461016f5780633be8c52c146101ac5780633f4ba83a146101da57600080fd5b3661013557005b600080fd5b61014d610148366004612338565b6103fe565b005b34801561015b57600080fd5b5061014d61016a36600461236d565b6105f0565b34801561017b57600080fd5b5061018f61018a3660046123a6565b61068a565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101b857600080fd5b506101cc6101c73660046123cd565b6106b4565b6040519081526020016101a3565b3480156101e657600080fd5b5061014d610743565b3480156101fb57600080fd5b506006546102099060ff1681565b60405190151581526020016101a3565b34801561022557600080fd5b5061014d61023436600461243f565b610755565b34801561024557600080fd5b5061014d610254366004612481565b6108f6565b34801561026557600080fd5b50600154600160a01b900460ff16610209565b34801561028457600080fd5b5060065461018f9061010090046001600160a01b031681565b3480156102a957600080fd5b5061014d61090a565b3480156102be57600080fd5b5061014d61091c565b3480156102d357600080fd5b5061014d6102e236600461266f565b61092c565b3480156102f357600080fd5b5061014d610302366004612716565b610db0565b34801561031357600080fd5b506001546001600160a01b031661018f565b34801561033157600080fd5b5061014d61034036600461278d565b610ff7565b34801561035157600080fd5b506101cc610360366004612802565b61103f565b34801561037157600080fd5b50610209610380366004612481565b6001600160a01b031660009081526003602052604090205460ff1690565b3480156103aa57600080fd5b5061014d6103b9366004612857565b6110ca565b3480156103ca57600080fd5b5061014d6103d9366004612481565b611515565b3480156103ea57600080fd5b5061014d6103f93660046128d1565b61158b565b6104066115da565b61040e611633565b61041783611680565b1561044f5781341461044a5760405163cfdff0eb60e01b8152600481018390523460248201526044015b60405180910390fd5b61059b565b341561047057604051635571d9ed60e11b8152346004820152602401610441565b6040516370a0823160e01b815230600482015283906000906001600160a01b038316906370a0823190602401602060405180830381865afa1580156104b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104dd91906128ee565b90506104f46001600160a01b0383163330876116a2565b6040516370a0823160e01b815230600482015260009082906001600160a01b038516906370a0823190602401602060405180830381865afa15801561053d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056191906128ee565b61056b919061291d565b905084811461059757604051632daa6d9560e21b81526004810186905260248101829052604401610441565b5050505b60408051838152602081018390526001600160a01b0385169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a36105eb6001600055565b505050565b6105f8611713565b6001600160a01b038216158061061557506001600160a01b038116155b1561063357604051636b093aad60e01b815260040160405180910390fd5b6001600160a01b0382811660008181526005602052604080822080546001600160a01b0319169486169485179055517f06c89f9d10102f3c193eb0acecda53739a03fee0dac5b3c380bb161c53ffabe89190a35050565b6002818154811061069a57600080fd5b6000918252602090912001546001600160a01b0316905081565b60405164045524332360dc1b60208201526bffffffffffffffffffffffff19606089811b821660258401526039830189905287811b82166059840152606d8301879052608d830186905230901b1660ad82015260c1810183905281151560f81b60e182015260009060e2015b604051602081830303815290604052805190602001209050979650505050505050565b61074b611713565b61075361176d565b565b61075d611713565b6107656115da565b6001600160a01b03811661078c5760405163d27b444360e01b815260040160405180910390fd5b8161079684611680565b156107e4574760008290036107a9578091505b808211156107d45760405163cf47918160e01b81526004810182905260248101839052604401610441565b6107de83836117c2565b5061089e565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa15801561082b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061084f91906128ee565b90508160000361085d578091505b808211156108885760405163cf47918160e01b81526004810182905260248101839052604401610441565b61089c6001600160a01b0386168484611848565b505b816001600160a01b0316846001600160a01b03167ff24ef89f38eadc1bde50701ad6e4d6d11a2dc24f7cf834a486991f3883328504836040516108e391815260200190565b60405180910390a3506105eb6001600055565b6108fe611713565b61090781611878565b50565b610912611713565b61075360006118f9565b610924611713565b61075361194b565b6109346115da565b61093c611633565b60065461010090046001600160a01b031661096a5760405163dc42a3fb60e01b815260040160405180910390fd5b60065460ff1661098d5760405163f15b2b2760e01b815260040160405180910390fd5b815160028110156109bb576040516313bb3cdb60e31b81526004810182905260026024820152604401610441565b815181146109e957815160405163012077a360e01b8152610441918391600401918252602082015260400190565b6109f28361198e565b42871015610a1c5760405163300249d760e01b815260048101889052426024820152604401610441565b610a2589611680565b15610a4357604051635885ca3f60e11b815260040160405180910390fd5b6000600660019054906101000a90046001600160a01b03166001600160a01b031663c18920586040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610abc9190612930565b9050806001600160a01b03168a6001600160a01b031614610afb57604051635f8b555b60e11b81526001600160a01b038b166004820152602401610441565b6000610b0c8b8b8b8b8b8b4661103f565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c829052603c81209192505b84811015610c26576000878281518110610b5b57610b5b61294d565b602002602001015190506000610b8a84898581518110610b7d57610b7d61294d565b6020026020010151611a3b565b9050816001600160a01b0316816001600160a01b031614610bd85760405163c758c30f60e01b8152600481018490526001600160a01b03808416602483015282166044820152606401610441565b6001600160a01b03811660009081526003602052604090205460ff16610c1c57604051630ee53f8960e21b81526001600160a01b0382166004820152602401610441565b5050600101610b3f565b50600654610c4590839061010090046001600160a01b03168d8f611a61565b600654610c65906001600160a01b038e8116916101009004166000611ae7565b600654610c84906001600160a01b038e8116916101009004168d611ae7565b6006546040516333fa5b2160e21b81526001600160a01b038e81166004830152602482018e9052604482018a9052606482018b905260a06084830152600060a48301526101009092049091169063cfe96c849060c4016020604051808303816000875af1158015610cf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1d91906128ee565b50600654610d3e906001600160a01b038e8116916101009004166000611ae7565b600654604080518d8152602081018b90529081018990526001600160a01b036101009092048216918e16908b907fad6a72300306625666862b3004975f1c6b4a0e6dd6a8a8e98232e48e0a1e0ab19060600160405180910390a450505050610da66001600055565b5050505050505050565b610db86115da565b610dc0611633565b610dc987611680565b15610de757604051635885ca3f60e11b815260040160405180910390fd5b60405163d505accf60e01b81526001600160a01b038681166004830152306024830152604482018890526064820186905260ff8516608483015260a4820184905260c4820183905288169063d505accf9060e401600060405180830381600087803b158015610e5557600080fd5b505af1158015610e69573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152899250600091506001600160a01b038316906370a0823190602401602060405180830381865afa158015610eb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eda91906128ee565b9050610ef16001600160a01b03831688308b6116a2565b6040516370a0823160e01b815230600482015260009082906001600160a01b038516906370a0823190602401602060405180830381865afa158015610f3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5e91906128ee565b610f68919061291d565b9050888114610f9457604051632daa6d9560e21b8152600481018a905260248101829052604401610441565b896001600160a01b0316886001600160a01b03167fbadd3df771956dec3ac1cfe8ad41246f5905a217f5c5d2c005d365fc569f9cfe8b604051610fd991815260200190565b60405180910390a3505050610fee6001600055565b50505050505050565b610fff611713565b61103b828280806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250611bfc92505050565b5050565b6006546040516f05452414e534645525f544f5f504552560841b60208201526bffffffffffffffffffffffff19610100909204606090811b8316603083015289811b8316604483015260588201899052607882018890526098820187905260b8820186905260d8820185905230901b90911660f882015261010c810182905260009061012c01610720565b6110d26115da565b6110da611633565b6001600160a01b0388166111015760405163d27b444360e01b815260040160405180910390fd5b8151600281101561112f576040516313bb3cdb60e31b81526004810182905260026024820152604401610441565b8151811461115d57815160405163012077a360e01b8152610441918391600401918252602082015260400190565b6111668361198e565b428610156111905760405163300249d760e01b815260048101879052426024820152604401610441565b60006111a18a8a8a8a8a468b6106b4565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c829052603c81209192505b838110156112ae5760008682815181106111f0576111f061294d565b60200260200101519050600061121284888581518110610b7d57610b7d61294d565b9050816001600160a01b0316816001600160a01b0316146112605760405163c758c30f60e01b8152600481018490526001600160a01b03808416602483015282166044820152606401610441565b6001600160a01b03811660009081526003602052604090205460ff166112a457604051630ee53f8960e21b81526001600160a01b0382166004820152602401610441565b50506001016111d4565b506112bb828c8c8c611a61565b851561147e5760065460ff166112e45760405163c9c45e3960e01b815260040160405180910390fd5b6112ed89611680565b15611386576040516325e1606360e01b81526001600160a01b038c811660048301527f000000000000000000000000000000000000000000000000000000000000000016906325e16063908c9060240160206040518083038185885af115801561135b573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061138091906128ee565b506114af565b6001600160a01b03808a1660009081526005602052604090205416806113ca576040516399c0e26b60e01b81526001600160a01b038b166004820152602401610441565b604051637b3a3c8b60e01b81526001600160a01b0382811660048301528d81166024830152604482018d905260806064830152600060848301527f00000000000000000000000000000000000000000000000000000000000000001690637b3a3c8b9060a4016000604051808303816000875af115801561144f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114779190810190612987565b50506114af565b61148789611680565b1561149b576114968b8b6117c2565b6114af565b6114af6001600160a01b038a168c8c611848565b8a6001600160a01b0316896001600160a01b0316887f698b3b4d9efec3be6b032f0b3eb88d5b267b8dad6c7f66c70e16f9bd99c233ac8d8a6040516115009291909182521515602082015260400190565b60405180910390a4505050610da66001600055565b61151d611713565b6001600160a01b0381166115825760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610441565b610907816118f9565b611593611713565b6006805460ff19168215159081179091556040519081527f2c3b0c89ca0fd6fe43aa5f111a2cb9e82cc2ec981ee5b26c63a697c738a243ff9060200160405180910390a150565b60026000540361162c5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610441565b6002600055565b600154600160a01b900460ff16156107535760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610441565b6001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1490565b6040516001600160a01b038085166024830152831660448201526064810182905261170d9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611de9565b50505050565b6001546001600160a01b031633146107535760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610441565b611775611ebe565b6001805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6000826001600160a01b03168260405160006040518083038185875af1925050503d806000811461180f576040519150601f19603f3d011682016040523d82523d6000602084013e611814565b606091505b50509050806105eb5760405163296c17bb60e21b81526001600160a01b038416600482015260248101839052604401610441565b6040516001600160a01b0383166024820152604481018290526105eb90849063a9059cbb60e01b906064016116d6565b6001600160a01b03811661189f5760405163dc42a3fb60e01b815260040160405180910390fd5b600680546001600160a01b03838116610100818102610100600160a81b031985161790945560405193909204169182907fe51a331e9d357f65bce28e6d406d84c1067e7d44c5adeb031b1c47dd2e9767e890600090a35050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b611953611633565b6001805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586117a53390565b805160005b818110156105eb5760008382815181106119af576119af61294d565b6020026020010151905060008260016119c891906129fe565b90505b83811015611a31578481815181106119e5576119e561294d565b60200260200101516001600160a01b0316826001600160a01b031603611a2957604051637010e27960e11b81526001600160a01b0383166004820152602401610441565b6001016119cb565b5050600101611993565b6000806000611a4a8585611f0e565b91509150611a5781611f53565b5090505b92915050565b60008481526004602052604090206002810154600160a01b900460ff1615611a9f57604051630ad1a00960e11b815260048101869052602401610441565b60028101805482546001600160a01b0319166001600160a01b03968716178355600190920193909355600160a01b6001600160a81b0319909116919093161791909117905550565b801580611b615750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015611b3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b5f91906128ee565b155b611bcc5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610441565b6040516001600160a01b0383166024820152604481018290526105eb90849063095ea7b360e01b906064016116d6565b805160028114611c295760405163377ca99960e21b81526004810182905260026024820152604401610441565b611c328261198e565b60005b600254811015611c945760006003600060028481548110611c5857611c5861294d565b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055600101611c35565b50611ca1600260006122f1565b60005b81811015611d6a576000838281518110611cc057611cc061294d565b6020026020010151905060006001600160a01b0316816001600160a01b031603611d005760405163ed76175960e01b815260048101839052602401610441565b6002805460018181019092557f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0180546001600160a01b039093166001600160a01b031990931683179055600091825260036020526040909120805460ff19168217905501611ca4565b506002600181548110611d7f57611d7f61294d565b6000918252602082200154600280546001600160a01b03909216929091611da857611da861294d565b60009182526020822001546040516001600160a01b03909116917f028fc5ffa55a0b7bd9ee999be60b4ae77211c4381125e3a0cae1ba230645edca91a35050565b6000611e3e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661209d9092919063ffffffff16565b9050805160001480611e5f575080806020019051810190611e5f9190612a11565b6105eb5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610441565b600154600160a01b900460ff166107535760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610441565b6000808251604103611f445760208301516040840151606085015160001a611f38878285856120b4565b94509450505050611f4c565b506000905060025b9250929050565b6000816004811115611f6757611f67612a2e565b03611f6f5750565b6001816004811115611f8357611f83612a2e565b03611fd05760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610441565b6002816004811115611fe457611fe4612a2e565b036120315760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610441565b600381600481111561204557612045612a2e565b036109075760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610441565b60606120ac8484600085612178565b949350505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156120eb575060009050600361216f565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561213f573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166121685760006001925092505061216f565b9150600090505b94509492505050565b6060824710156121d95760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610441565b600080866001600160a01b031685876040516121f59190612a44565b60006040518083038185875af1925050503d8060008114612232576040519150601f19603f3d011682016040523d82523d6000602084013e612237565b606091505b509150915061224887838387612253565b979650505050505050565b606083156122c25782516000036122bb576001600160a01b0385163b6122bb5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610441565b50816120ac565b6120ac83838151156122d75781518083602001fd5b8060405162461bcd60e51b81526004016104419190612a60565b508054600082559060005260206000209081019061090791905b8082111561231f576000815560010161230b565b5090565b6001600160a01b038116811461090757600080fd5b60008060006060848603121561234d57600080fd5b833561235881612323565b95602085013595506040909401359392505050565b6000806040838503121561238057600080fd5b823561238b81612323565b9150602083013561239b81612323565b809150509250929050565b6000602082840312156123b857600080fd5b5035919050565b801515811461090757600080fd5b600080600080600080600060e0888a0312156123e857600080fd5b87356123f381612323565b965060208801359550604088013561240a81612323565b9450606088013593506080880135925060a0880135915060c088013561242f816123bf565b8091505092959891949750929550565b60008060006060848603121561245457600080fd5b833561245f81612323565b925060208401359150604084013561247681612323565b809150509250925092565b60006020828403121561249357600080fd5b813561249e81612323565b9392505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156124e4576124e46124a5565b604052919050565b600067ffffffffffffffff821115612506576125066124a5565b5060051b60200190565b600082601f83011261252157600080fd5b81356020612536612531836124ec565b6124bb565b8083825260208201915060208460051b87010193508684111561255857600080fd5b602086015b8481101561257d57803561257081612323565b835291830191830161255d565b509695505050505050565b600067ffffffffffffffff8211156125a2576125a26124a5565b50601f01601f191660200190565b600082601f8301126125c157600080fd5b813560206125d1612531836124ec565b82815260059290921b840181019181810190868411156125f057600080fd5b8286015b8481101561257d57803567ffffffffffffffff8111156126145760008081fd5b8701603f810189136126265760008081fd5b84810135604061263861253183612588565b8281528b8284860101111561264d5760008081fd5b82828501898301376000928101880192909252508452509183019183016125f4565b600080600080600080600080610100898b03121561268c57600080fd5b883561269781612323565b97506020890135965060408901359550606089013594506080890135935060a0890135925060c089013567ffffffffffffffff808211156126d757600080fd5b6126e38c838d01612510565b935060e08b01359150808211156126f957600080fd5b506127068b828c016125b0565b9150509295985092959890939650565b600080600080600080600060e0888a03121561273157600080fd5b873561273c81612323565b965060208801359550604088013561275381612323565b945060608801359350608088013560ff8116811461277057600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080602083850312156127a057600080fd5b823567ffffffffffffffff808211156127b857600080fd5b818501915085601f8301126127cc57600080fd5b8135818111156127db57600080fd5b8660208260051b85010111156127f057600080fd5b60209290920196919550909350505050565b600080600080600080600060e0888a03121561281d57600080fd5b873561282881612323565b9960208901359950604089013598606081013598506080810135975060a0810135965060c00135945092505050565b600080600080600080600080610100898b03121561287457600080fd5b883561287f81612323565b975060208901359650604089013561289681612323565b9550606089013594506080890135935060a08901356128b4816123bf565b925060c089013567ffffffffffffffff808211156126d757600080fd5b6000602082840312156128e357600080fd5b813561249e816123bf565b60006020828403121561290057600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b81810381811115611a5b57611a5b612907565b60006020828403121561294257600080fd5b815161249e81612323565b634e487b7160e01b600052603260045260246000fd5b60005b8381101561297e578181015183820152602001612966565b50506000910152565b60006020828403121561299957600080fd5b815167ffffffffffffffff8111156129b057600080fd5b8201601f810184136129c157600080fd5b80516129cf61253182612588565b8181528560208385010111156129e457600080fd5b6129f5826020830160208601612963565b95945050505050565b80820180821115611a5b57611a5b612907565b600060208284031215612a2357600080fd5b815161249e816123bf565b634e487b7160e01b600052602160045260246000fd5b60008251612a56818460208701612963565b9190910192915050565b6020815260008251806020840152612a7f816040850160208701612963565b601f01601f1916919091016040019291505056fea164736f6c6343000818000a00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000d8e20462edce38434616cc6a6a560bb76b582ed8000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e583100000000000000000000000000000000000000000000000000000000000000640000000000000000000000008bd5e5c114375f8a7a034f96fe2e67b31eeb48ac000000000000000000000000a8f2d245e4e1c6cb9cfa6bf9037abcb29e0b869c00000000000000000000000000000000000000000000000000000000000000020000000000000000000000003213c89d1dc06389010af5f0691854c48b0cd182000000000000000000000000a6caacff361f7aad226a4dea5e3cf28af07d773e
Deployed Bytecode
0x60806040526004361061012e5760003560e01c8063715018a6116100ab578063a37726621161006f578063a377266214610325578063b7947ef514610345578063cabb9e7a14610365578063dc2b6df01461039e578063f2fde38b146103be578063f3927487146103de57600080fd5b8063715018a61461029d5780638456cb59146102b257806385217809146102c757806388dfa7a6146102e75780638da5cb5b1461030757600080fd5b80634fe08484116100f25780634fe08484146101ef578063551512de14610219578063570828d4146102395780635c975abb146102595780636273010d1461027857600080fd5b80630efe6a8b1461013a5780631c2453a61461014f5780632079fb9a1461016f5780633be8c52c146101ac5780633f4ba83a146101da57600080fd5b3661013557005b600080fd5b61014d610148366004612338565b6103fe565b005b34801561015b57600080fd5b5061014d61016a36600461236d565b6105f0565b34801561017b57600080fd5b5061018f61018a3660046123a6565b61068a565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101b857600080fd5b506101cc6101c73660046123cd565b6106b4565b6040519081526020016101a3565b3480156101e657600080fd5b5061014d610743565b3480156101fb57600080fd5b506006546102099060ff1681565b60405190151581526020016101a3565b34801561022557600080fd5b5061014d61023436600461243f565b610755565b34801561024557600080fd5b5061014d610254366004612481565b6108f6565b34801561026557600080fd5b50600154600160a01b900460ff16610209565b34801561028457600080fd5b5060065461018f9061010090046001600160a01b031681565b3480156102a957600080fd5b5061014d61090a565b3480156102be57600080fd5b5061014d61091c565b3480156102d357600080fd5b5061014d6102e236600461266f565b61092c565b3480156102f357600080fd5b5061014d610302366004612716565b610db0565b34801561031357600080fd5b506001546001600160a01b031661018f565b34801561033157600080fd5b5061014d61034036600461278d565b610ff7565b34801561035157600080fd5b506101cc610360366004612802565b61103f565b34801561037157600080fd5b50610209610380366004612481565b6001600160a01b031660009081526003602052604090205460ff1690565b3480156103aa57600080fd5b5061014d6103b9366004612857565b6110ca565b3480156103ca57600080fd5b5061014d6103d9366004612481565b611515565b3480156103ea57600080fd5b5061014d6103f93660046128d1565b61158b565b6104066115da565b61040e611633565b61041783611680565b1561044f5781341461044a5760405163cfdff0eb60e01b8152600481018390523460248201526044015b60405180910390fd5b61059b565b341561047057604051635571d9ed60e11b8152346004820152602401610441565b6040516370a0823160e01b815230600482015283906000906001600160a01b038316906370a0823190602401602060405180830381865afa1580156104b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104dd91906128ee565b90506104f46001600160a01b0383163330876116a2565b6040516370a0823160e01b815230600482015260009082906001600160a01b038516906370a0823190602401602060405180830381865afa15801561053d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056191906128ee565b61056b919061291d565b905084811461059757604051632daa6d9560e21b81526004810186905260248101829052604401610441565b5050505b60408051838152602081018390526001600160a01b0385169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a36105eb6001600055565b505050565b6105f8611713565b6001600160a01b038216158061061557506001600160a01b038116155b1561063357604051636b093aad60e01b815260040160405180910390fd5b6001600160a01b0382811660008181526005602052604080822080546001600160a01b0319169486169485179055517f06c89f9d10102f3c193eb0acecda53739a03fee0dac5b3c380bb161c53ffabe89190a35050565b6002818154811061069a57600080fd5b6000918252602090912001546001600160a01b0316905081565b60405164045524332360dc1b60208201526bffffffffffffffffffffffff19606089811b821660258401526039830189905287811b82166059840152606d8301879052608d830186905230901b1660ad82015260c1810183905281151560f81b60e182015260009060e2015b604051602081830303815290604052805190602001209050979650505050505050565b61074b611713565b61075361176d565b565b61075d611713565b6107656115da565b6001600160a01b03811661078c5760405163d27b444360e01b815260040160405180910390fd5b8161079684611680565b156107e4574760008290036107a9578091505b808211156107d45760405163cf47918160e01b81526004810182905260248101839052604401610441565b6107de83836117c2565b5061089e565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa15801561082b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061084f91906128ee565b90508160000361085d578091505b808211156108885760405163cf47918160e01b81526004810182905260248101839052604401610441565b61089c6001600160a01b0386168484611848565b505b816001600160a01b0316846001600160a01b03167ff24ef89f38eadc1bde50701ad6e4d6d11a2dc24f7cf834a486991f3883328504836040516108e391815260200190565b60405180910390a3506105eb6001600055565b6108fe611713565b61090781611878565b50565b610912611713565b61075360006118f9565b610924611713565b61075361194b565b6109346115da565b61093c611633565b60065461010090046001600160a01b031661096a5760405163dc42a3fb60e01b815260040160405180910390fd5b60065460ff1661098d5760405163f15b2b2760e01b815260040160405180910390fd5b815160028110156109bb576040516313bb3cdb60e31b81526004810182905260026024820152604401610441565b815181146109e957815160405163012077a360e01b8152610441918391600401918252602082015260400190565b6109f28361198e565b42871015610a1c5760405163300249d760e01b815260048101889052426024820152604401610441565b610a2589611680565b15610a4357604051635885ca3f60e11b815260040160405180910390fd5b6000600660019054906101000a90046001600160a01b03166001600160a01b031663c18920586040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610abc9190612930565b9050806001600160a01b03168a6001600160a01b031614610afb57604051635f8b555b60e11b81526001600160a01b038b166004820152602401610441565b6000610b0c8b8b8b8b8b8b4661103f565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c829052603c81209192505b84811015610c26576000878281518110610b5b57610b5b61294d565b602002602001015190506000610b8a84898581518110610b7d57610b7d61294d565b6020026020010151611a3b565b9050816001600160a01b0316816001600160a01b031614610bd85760405163c758c30f60e01b8152600481018490526001600160a01b03808416602483015282166044820152606401610441565b6001600160a01b03811660009081526003602052604090205460ff16610c1c57604051630ee53f8960e21b81526001600160a01b0382166004820152602401610441565b5050600101610b3f565b50600654610c4590839061010090046001600160a01b03168d8f611a61565b600654610c65906001600160a01b038e8116916101009004166000611ae7565b600654610c84906001600160a01b038e8116916101009004168d611ae7565b6006546040516333fa5b2160e21b81526001600160a01b038e81166004830152602482018e9052604482018a9052606482018b905260a06084830152600060a48301526101009092049091169063cfe96c849060c4016020604051808303816000875af1158015610cf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1d91906128ee565b50600654610d3e906001600160a01b038e8116916101009004166000611ae7565b600654604080518d8152602081018b90529081018990526001600160a01b036101009092048216918e16908b907fad6a72300306625666862b3004975f1c6b4a0e6dd6a8a8e98232e48e0a1e0ab19060600160405180910390a450505050610da66001600055565b5050505050505050565b610db86115da565b610dc0611633565b610dc987611680565b15610de757604051635885ca3f60e11b815260040160405180910390fd5b60405163d505accf60e01b81526001600160a01b038681166004830152306024830152604482018890526064820186905260ff8516608483015260a4820184905260c4820183905288169063d505accf9060e401600060405180830381600087803b158015610e5557600080fd5b505af1158015610e69573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152899250600091506001600160a01b038316906370a0823190602401602060405180830381865afa158015610eb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eda91906128ee565b9050610ef16001600160a01b03831688308b6116a2565b6040516370a0823160e01b815230600482015260009082906001600160a01b038516906370a0823190602401602060405180830381865afa158015610f3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5e91906128ee565b610f68919061291d565b9050888114610f9457604051632daa6d9560e21b8152600481018a905260248101829052604401610441565b896001600160a01b0316886001600160a01b03167fbadd3df771956dec3ac1cfe8ad41246f5905a217f5c5d2c005d365fc569f9cfe8b604051610fd991815260200190565b60405180910390a3505050610fee6001600055565b50505050505050565b610fff611713565b61103b828280806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250611bfc92505050565b5050565b6006546040516f05452414e534645525f544f5f504552560841b60208201526bffffffffffffffffffffffff19610100909204606090811b8316603083015289811b8316604483015260588201899052607882018890526098820187905260b8820186905260d8820185905230901b90911660f882015261010c810182905260009061012c01610720565b6110d26115da565b6110da611633565b6001600160a01b0388166111015760405163d27b444360e01b815260040160405180910390fd5b8151600281101561112f576040516313bb3cdb60e31b81526004810182905260026024820152604401610441565b8151811461115d57815160405163012077a360e01b8152610441918391600401918252602082015260400190565b6111668361198e565b428610156111905760405163300249d760e01b815260048101879052426024820152604401610441565b60006111a18a8a8a8a8a468b6106b4565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c829052603c81209192505b838110156112ae5760008682815181106111f0576111f061294d565b60200260200101519050600061121284888581518110610b7d57610b7d61294d565b9050816001600160a01b0316816001600160a01b0316146112605760405163c758c30f60e01b8152600481018490526001600160a01b03808416602483015282166044820152606401610441565b6001600160a01b03811660009081526003602052604090205460ff166112a457604051630ee53f8960e21b81526001600160a01b0382166004820152602401610441565b50506001016111d4565b506112bb828c8c8c611a61565b851561147e5760065460ff166112e45760405163c9c45e3960e01b815260040160405180910390fd5b6112ed89611680565b15611386576040516325e1606360e01b81526001600160a01b038c811660048301527f000000000000000000000000000000000000000000000000000000000000006416906325e16063908c9060240160206040518083038185885af115801561135b573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061138091906128ee565b506114af565b6001600160a01b03808a1660009081526005602052604090205416806113ca576040516399c0e26b60e01b81526001600160a01b038b166004820152602401610441565b604051637b3a3c8b60e01b81526001600160a01b0382811660048301528d81166024830152604482018d905260806064830152600060848301527f0000000000000000000000008bd5e5c114375f8a7a034f96fe2e67b31eeb48ac1690637b3a3c8b9060a4016000604051808303816000875af115801561144f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114779190810190612987565b50506114af565b61148789611680565b1561149b576114968b8b6117c2565b6114af565b6114af6001600160a01b038a168c8c611848565b8a6001600160a01b0316896001600160a01b0316887f698b3b4d9efec3be6b032f0b3eb88d5b267b8dad6c7f66c70e16f9bd99c233ac8d8a6040516115009291909182521515602082015260400190565b60405180910390a4505050610da66001600055565b61151d611713565b6001600160a01b0381166115825760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610441565b610907816118f9565b611593611713565b6006805460ff19168215159081179091556040519081527f2c3b0c89ca0fd6fe43aa5f111a2cb9e82cc2ec981ee5b26c63a697c738a243ff9060200160405180910390a150565b60026000540361162c5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610441565b6002600055565b600154600160a01b900460ff16156107535760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610441565b6001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1490565b6040516001600160a01b038085166024830152831660448201526064810182905261170d9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611de9565b50505050565b6001546001600160a01b031633146107535760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610441565b611775611ebe565b6001805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6000826001600160a01b03168260405160006040518083038185875af1925050503d806000811461180f576040519150601f19603f3d011682016040523d82523d6000602084013e611814565b606091505b50509050806105eb5760405163296c17bb60e21b81526001600160a01b038416600482015260248101839052604401610441565b6040516001600160a01b0383166024820152604481018290526105eb90849063a9059cbb60e01b906064016116d6565b6001600160a01b03811661189f5760405163dc42a3fb60e01b815260040160405180910390fd5b600680546001600160a01b03838116610100818102610100600160a81b031985161790945560405193909204169182907fe51a331e9d357f65bce28e6d406d84c1067e7d44c5adeb031b1c47dd2e9767e890600090a35050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b611953611633565b6001805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586117a53390565b805160005b818110156105eb5760008382815181106119af576119af61294d565b6020026020010151905060008260016119c891906129fe565b90505b83811015611a31578481815181106119e5576119e561294d565b60200260200101516001600160a01b0316826001600160a01b031603611a2957604051637010e27960e11b81526001600160a01b0383166004820152602401610441565b6001016119cb565b5050600101611993565b6000806000611a4a8585611f0e565b91509150611a5781611f53565b5090505b92915050565b60008481526004602052604090206002810154600160a01b900460ff1615611a9f57604051630ad1a00960e11b815260048101869052602401610441565b60028101805482546001600160a01b0319166001600160a01b03968716178355600190920193909355600160a01b6001600160a81b0319909116919093161791909117905550565b801580611b615750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015611b3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b5f91906128ee565b155b611bcc5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610441565b6040516001600160a01b0383166024820152604481018290526105eb90849063095ea7b360e01b906064016116d6565b805160028114611c295760405163377ca99960e21b81526004810182905260026024820152604401610441565b611c328261198e565b60005b600254811015611c945760006003600060028481548110611c5857611c5861294d565b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055600101611c35565b50611ca1600260006122f1565b60005b81811015611d6a576000838281518110611cc057611cc061294d565b6020026020010151905060006001600160a01b0316816001600160a01b031603611d005760405163ed76175960e01b815260048101839052602401610441565b6002805460018181019092557f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0180546001600160a01b039093166001600160a01b031990931683179055600091825260036020526040909120805460ff19168217905501611ca4565b506002600181548110611d7f57611d7f61294d565b6000918252602082200154600280546001600160a01b03909216929091611da857611da861294d565b60009182526020822001546040516001600160a01b03909116917f028fc5ffa55a0b7bd9ee999be60b4ae77211c4381125e3a0cae1ba230645edca91a35050565b6000611e3e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661209d9092919063ffffffff16565b9050805160001480611e5f575080806020019051810190611e5f9190612a11565b6105eb5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610441565b600154600160a01b900460ff166107535760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610441565b6000808251604103611f445760208301516040840151606085015160001a611f38878285856120b4565b94509450505050611f4c565b506000905060025b9250929050565b6000816004811115611f6757611f67612a2e565b03611f6f5750565b6001816004811115611f8357611f83612a2e565b03611fd05760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610441565b6002816004811115611fe457611fe4612a2e565b036120315760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610441565b600381600481111561204557612045612a2e565b036109075760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610441565b60606120ac8484600085612178565b949350505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156120eb575060009050600361216f565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561213f573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166121685760006001925092505061216f565b9150600090505b94509492505050565b6060824710156121d95760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610441565b600080866001600160a01b031685876040516121f59190612a44565b60006040518083038185875af1925050503d8060008114612232576040519150601f19603f3d011682016040523d82523d6000602084013e612237565b606091505b509150915061224887838387612253565b979650505050505050565b606083156122c25782516000036122bb576001600160a01b0385163b6122bb5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610441565b50816120ac565b6120ac83838151156122d75781518083602001fd5b8060405162461bcd60e51b81526004016104419190612a60565b508054600082559060005260206000209081019061090791905b8082111561231f576000815560010161230b565b5090565b6001600160a01b038116811461090757600080fd5b60008060006060848603121561234d57600080fd5b833561235881612323565b95602085013595506040909401359392505050565b6000806040838503121561238057600080fd5b823561238b81612323565b9150602083013561239b81612323565b809150509250929050565b6000602082840312156123b857600080fd5b5035919050565b801515811461090757600080fd5b600080600080600080600060e0888a0312156123e857600080fd5b87356123f381612323565b965060208801359550604088013561240a81612323565b9450606088013593506080880135925060a0880135915060c088013561242f816123bf565b8091505092959891949750929550565b60008060006060848603121561245457600080fd5b833561245f81612323565b925060208401359150604084013561247681612323565b809150509250925092565b60006020828403121561249357600080fd5b813561249e81612323565b9392505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156124e4576124e46124a5565b604052919050565b600067ffffffffffffffff821115612506576125066124a5565b5060051b60200190565b600082601f83011261252157600080fd5b81356020612536612531836124ec565b6124bb565b8083825260208201915060208460051b87010193508684111561255857600080fd5b602086015b8481101561257d57803561257081612323565b835291830191830161255d565b509695505050505050565b600067ffffffffffffffff8211156125a2576125a26124a5565b50601f01601f191660200190565b600082601f8301126125c157600080fd5b813560206125d1612531836124ec565b82815260059290921b840181019181810190868411156125f057600080fd5b8286015b8481101561257d57803567ffffffffffffffff8111156126145760008081fd5b8701603f810189136126265760008081fd5b84810135604061263861253183612588565b8281528b8284860101111561264d5760008081fd5b82828501898301376000928101880192909252508452509183019183016125f4565b600080600080600080600080610100898b03121561268c57600080fd5b883561269781612323565b97506020890135965060408901359550606089013594506080890135935060a0890135925060c089013567ffffffffffffffff808211156126d757600080fd5b6126e38c838d01612510565b935060e08b01359150808211156126f957600080fd5b506127068b828c016125b0565b9150509295985092959890939650565b600080600080600080600060e0888a03121561273157600080fd5b873561273c81612323565b965060208801359550604088013561275381612323565b945060608801359350608088013560ff8116811461277057600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080602083850312156127a057600080fd5b823567ffffffffffffffff808211156127b857600080fd5b818501915085601f8301126127cc57600080fd5b8135818111156127db57600080fd5b8660208260051b85010111156127f057600080fd5b60209290920196919550909350505050565b600080600080600080600060e0888a03121561281d57600080fd5b873561282881612323565b9960208901359950604089013598606081013598506080810135975060a0810135965060c00135945092505050565b600080600080600080600080610100898b03121561287457600080fd5b883561287f81612323565b975060208901359650604089013561289681612323565b9550606089013594506080890135935060a08901356128b4816123bf565b925060c089013567ffffffffffffffff808211156126d757600080fd5b6000602082840312156128e357600080fd5b813561249e816123bf565b60006020828403121561290057600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b81810381811115611a5b57611a5b612907565b60006020828403121561294257600080fd5b815161249e81612323565b634e487b7160e01b600052603260045260246000fd5b60005b8381101561297e578181015183820152602001612966565b50506000910152565b60006020828403121561299957600080fd5b815167ffffffffffffffff8111156129b057600080fd5b8201601f810184136129c157600080fd5b80516129cf61253182612588565b8181528560208385010111156129e457600080fd5b6129f5826020830160208601612963565b95945050505050565b80820180821115611a5b57611a5b612907565b600060208284031215612a2357600080fd5b815161249e816123bf565b634e487b7160e01b600052602160045260246000fd5b60008251612a56818460208701612963565b9190910192915050565b6020815260008251806020840152612a7f816040850160208701612963565b601f01601f1916919091016040019291505056fea164736f6c6343000818000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000d8e20462edce38434616cc6a6a560bb76b582ed8000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e583100000000000000000000000000000000000000000000000000000000000000640000000000000000000000008bd5e5c114375f8a7a034f96fe2e67b31eeb48ac000000000000000000000000a8f2d245e4e1c6cb9cfa6bf9037abcb29e0b869c00000000000000000000000000000000000000000000000000000000000000020000000000000000000000003213c89d1dc06389010af5f0691854c48b0cd182000000000000000000000000a6caacff361f7aad226a4dea5e3cf28af07d773e
-----Decoded View---------------
Arg [0] : allowedSigners (address[]): 0x3213c89D1dC06389010af5f0691854C48B0cD182,0xA6CAAcFF361f7aad226A4DEa5e3CF28af07D773E
Arg [1] : edgeUsdc (address): 0xd8e20462EDCe38434616Cc6A6a560BB76B582ED8
Arg [2] : arbUsdc (address): 0xaf88d065e77c8cC2239327C5EDb3A432268e5831
Arg [3] : arbSys (address): 0x0000000000000000000000000000000000000064
Arg [4] : l2Gateway (address): 0x8BD5e5C114375f8A7A034f96fE2e67B31EEb48Ac
Arg [5] : owner_ (address): 0xA8f2D245E4e1c6CB9cFa6bF9037aBcB29E0b869c
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [1] : 000000000000000000000000d8e20462edce38434616cc6a6a560bb76b582ed8
Arg [2] : 000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000064
Arg [4] : 0000000000000000000000008bd5e5c114375f8a7a034f96fe2e67b31eeb48ac
Arg [5] : 000000000000000000000000a8f2d245e4e1c6cb9cfa6bf9037abcb29e0b869c
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [7] : 0000000000000000000000003213c89d1dc06389010af5f0691854c48b0cd182
Arg [8] : 000000000000000000000000a6caacff361f7aad226a4dea5e3cf28af07d773e
Loading...
Loading
Loading...
Loading
Net Worth in USD
$7,075,686.72
Net Worth in ETH
3,574.198434
Token Allocations
ETH
84.24%
LIT
12.10%
LINK
1.07%
Others
2.59%
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 84.24% | $1,979.66 | 3,010.9316 | $5,960,611.68 | |
| ETH | 12.10% | $1.38 | 620,295.1408 | $856,007.29 | |
| ETH | 1.07% | $8.8 | 8,638.7701 | $76,021.18 | |
| ETH | 0.84% | $115.17 | 514.8367 | $59,293.74 | |
| ETH | 0.54% | $0.105348 | 365,157.2932 | $38,468.59 | |
| ETH | 0.49% | $3.81 | 9,107.2003 | $34,698.43 | |
| ETH | 0.29% | $2.15 | 9,641.6222 | $20,729.49 | |
| ETH | 0.24% | $0.256365 | 66,662.6931 | $17,089.98 | |
| ETH | 0.18% | $0.000004 | 3,646,431,769.1753 | $12,762.51 | |
| ETH | <0.01% | $76.6 | 0.05 | $3.83 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.