Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- Swap
- Optimization enabled
- true
- Compiler version
- v0.8.23+commit.f704f362
- Optimization runs
- 999999
- EVM Version
- paris
- Verified at
- 2024-04-16T10:42:04.823730Z
Constructor Arguments
0x000000000000000000000000000000000000000000000000000000000000008036372b07000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032000000000000000000000000c99c045f61cf8f628314170764a27cdeb75d2e2800000000000000000000000000000000000000000000000000000000000000030000000000000000000000002925817ae9be8251e6b281bdb1fad175c671c32c0000000000000000000000003b07b2bb728d0bf1271c19c22a997d8407e9aa950000000000000000000000008720c4573ab7e89c9e26951c49f5b80b8429e016
contracts/Swap.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import "@openzeppelin/contracts/interfaces/IERC2981.sol";
import "./interfaces/ISwap.sol";
/**
* @title AirSwap: Atomic Token Swap
* @notice https://www.airswap.io/
*/
contract Swap is ISwap, Ownable2Step, EIP712 {
bytes32 private constant ORDER_TYPEHASH =
keccak256(
abi.encodePacked(
"Order(uint256 nonce,uint256 expiry,uint256 protocolFee,Party signer,Party sender,address affiliateWallet,uint256 affiliateAmount)",
"Party(address wallet,address token,bytes4 kind,uint256 id,uint256 amount)"
)
);
bytes32 private constant PARTY_TYPEHASH =
keccak256(
"Party(address wallet,address token,bytes4 kind,uint256 id,uint256 amount)"
);
// Domain name and version for use in EIP712 signatures
string public constant DOMAIN_NAME = "SWAP";
string public constant DOMAIN_VERSION = "4.2";
uint256 public immutable DOMAIN_CHAIN_ID;
bytes32 public immutable DOMAIN_SEPARATOR;
uint256 public constant FEE_DIVISOR = 10000;
uint256 private constant MAX_ERROR_COUNT = 16;
/**
* @notice Double mapping of signers to nonce groups to nonce states
* @dev The nonce group is computed as nonce / 256, so each group of 256 sequential nonces uses the same key
* @dev The nonce states are encoded as 256 bits, for each nonce in the group 0 means available and 1 means used
*/
mapping(address => mapping(uint256 => uint256)) private _nonceGroups;
// Mapping of signer to authorized signatory
mapping(address => address) public override authorized;
// Mapping of signatory address to a minimum valid nonce
mapping(address => uint256) public signatoryMinimumNonce;
uint256 public protocolFee;
address public protocolFeeWallet;
bytes4 public immutable requiredSenderKind;
// Mapping of ERC165 interface ID to token adapter
mapping(bytes4 => IAdapter) public adapters;
/**
* @notice Swap constructor
* @dev Sets domain and version for EIP712 signatures
* @param _adapters IAdapter[] array of token adapters
* @param _protocolFee uin256 protocol fee to be assessed on swaps
* @param _protocolFeeWallet address destination for protocol fees
*/
constructor(
IAdapter[] memory _adapters,
bytes4 _requiredSenderKind,
uint256 _protocolFee,
address _protocolFeeWallet
) EIP712(DOMAIN_NAME, DOMAIN_VERSION) {
if (_protocolFee >= FEE_DIVISOR) revert FeeInvalid();
if (_protocolFeeWallet == address(0)) revert FeeWalletInvalid();
if (_adapters.length == 0) revert AdaptersInvalid();
DOMAIN_CHAIN_ID = block.chainid;
DOMAIN_SEPARATOR = _domainSeparatorV4();
uint256 adaptersLength = _adapters.length;
for (uint256 i; i < adaptersLength; ) {
adapters[_adapters[i].interfaceId()] = _adapters[i];
unchecked {
++i;
}
}
requiredSenderKind = _requiredSenderKind;
protocolFee = _protocolFee;
protocolFeeWallet = _protocolFeeWallet;
}
/**
* @notice Atomic Token Swap
* @param recipient address Wallet to receive sender proceeds
* @param maxRoyalty uint256 Max to avoid unexpected royalties
* @param order Order to settle
*/
function swap(
address recipient,
uint256 maxRoyalty,
Order calldata order
) external {
// Ensure order is valid for signer
_check(order);
// Ensure msg.sender matches order if specified
if (order.sender.wallet != address(0) && order.sender.wallet != msg.sender)
revert SenderInvalid();
// Transfer from sender to signer
_transfer(
msg.sender,
order.signer.wallet,
order.sender.amount,
order.sender.id,
order.sender.token,
order.sender.kind
);
// Transfer from signer to recipient
_transfer(
order.signer.wallet,
recipient,
order.signer.amount,
order.signer.id,
order.signer.token,
order.signer.kind
);
// Transfer from sender to affiliate if specified
if (order.affiliateWallet != address(0)) {
_transfer(
msg.sender,
order.affiliateWallet,
order.affiliateAmount,
order.sender.id,
order.sender.token,
order.sender.kind
);
}
// Transfer protocol fee from sender
uint256 protocolFeeAmount = (order.sender.amount * protocolFee) /
FEE_DIVISOR;
if (protocolFeeAmount > 0) {
_transfer(
msg.sender,
protocolFeeWallet,
protocolFeeAmount,
order.sender.id,
order.sender.token,
order.sender.kind
);
}
// Transfer royalty from sender if required by signer token
if (supportsRoyalties(order.signer.token)) {
address royaltyRecipient;
uint256 royaltyAmount;
(royaltyRecipient, royaltyAmount) = IERC2981(order.signer.token)
.royaltyInfo(order.signer.id, order.sender.amount);
if (royaltyAmount > 0) {
if (royaltyAmount > maxRoyalty) revert RoyaltyExceedsMax(royaltyAmount);
_transfer(
msg.sender,
royaltyRecipient,
royaltyAmount,
order.sender.id,
order.sender.token,
order.sender.kind
);
}
}
emit Swap(
order.nonce,
order.signer.wallet,
order.signer.amount,
order.signer.id,
order.signer.token,
msg.sender,
order.sender.amount,
order.sender.id,
order.sender.token,
order.affiliateWallet,
order.affiliateAmount
);
}
/**
* @notice Set the protocol fee
* @param _protocolFee uint256 Value of the fee in basis points
*/
function setProtocolFee(uint256 _protocolFee) external onlyOwner {
// Ensure the fee is less than divisor
if (_protocolFee >= FEE_DIVISOR) revert FeeInvalid();
protocolFee = _protocolFee;
emit SetProtocolFee(_protocolFee);
}
/**
* @notice Set the protocol fee wallet
* @param _protocolFeeWallet address Wallet to transfer fee to
*/
function setProtocolFeeWallet(address _protocolFeeWallet) external onlyOwner {
// Ensure the new fee wallet is not null
if (_protocolFeeWallet == address(0)) revert FeeWalletInvalid();
protocolFeeWallet = _protocolFeeWallet;
emit SetProtocolFeeWallet(_protocolFeeWallet);
}
/**
* @notice Authorize a signer
* @param signatory address Wallet of the signer to authorize
* @dev Emits an Authorize event
*/
function authorize(address signatory) external override {
if (signatory == address(0)) revert SignatoryInvalid();
authorized[msg.sender] = signatory;
emit Authorize(signatory, msg.sender);
}
/**
* @notice Revoke the signatory
* @dev Emits a Revoke event
*/
function revoke() external override {
address tmp = authorized[msg.sender];
delete authorized[msg.sender];
emit Revoke(tmp, msg.sender);
}
/**
* @notice Cancel one or more nonces
* @dev Cancelled nonces are marked as used
* @dev Emits a Cancel event
* @dev Out of gas may occur in arrays of length > 400
* @param nonces uint256[] List of nonces to cancel
*/
function cancel(uint256[] calldata nonces) external override {
for (uint256 i; i < nonces.length; ) {
uint256 nonce = nonces[i];
_markNonceAsUsed(msg.sender, nonce);
emit Cancel(nonce, msg.sender);
unchecked {
++i;
}
}
}
/**
* @notice Cancels all orders below a nonce value
* @dev Emits a CancelUpTo event
* @param minimumNonce uint256 Minimum valid nonce
*/
function cancelUpTo(uint256 minimumNonce) external {
signatoryMinimumNonce[msg.sender] = minimumNonce;
emit CancelUpTo(minimumNonce, msg.sender);
}
/**
* @notice Checks an order for errors
* @param senderWallet address Wallet that would send the order
* @param order Order that would be settled
* @return bytes32[] errors
*/
function check(
address senderWallet,
Order calldata order
) external view returns (bytes32[] memory) {
bytes32[] memory errors = new bytes32[](MAX_ERROR_COUNT);
uint256 count;
if (DOMAIN_CHAIN_ID != block.chainid) {
errors[count++] = "ChainIdChanged";
}
// Validate as the authorized signatory if set
address signatory = order.signer.wallet;
if (authorized[signatory] != address(0)) {
signatory = authorized[signatory];
}
if (
!SignatureChecker.isValidSignatureNow(
signatory,
_getOrderHash(order),
abi.encodePacked(order.r, order.s, order.v)
)
) {
errors[count++] = "Unauthorized";
} else if (nonceUsed(signatory, order.nonce)) {
errors[count++] = "NonceAlreadyUsed";
} else if (order.nonce < signatoryMinimumNonce[signatory]) {
errors[count++] = "NonceTooLow";
}
if (order.expiry < block.timestamp) {
errors[count++] = "OrderExpired";
}
if (
order.sender.wallet != address(0) && order.sender.wallet != senderWallet
) {
errors[count++] = "SenderInvalid";
}
IAdapter senderTokenAdapter = adapters[order.sender.kind];
if (address(senderTokenAdapter) == address(0)) {
errors[count++] = "SenderTokenKindUnknown";
} else {
if (order.sender.kind != requiredSenderKind) {
errors[count++] = "SenderTokenInvalid";
} else {
uint256 protocolFeeAmount = (order.sender.amount * protocolFee) /
FEE_DIVISOR;
uint256 totalSenderAmount = order.sender.amount +
protocolFeeAmount +
order.affiliateAmount;
if (supportsRoyalties(order.signer.token)) {
(, uint256 royaltyAmount) = IERC2981(order.signer.token).royaltyInfo(
order.signer.id,
order.sender.amount
);
totalSenderAmount += royaltyAmount;
}
Party memory sender = Party(
senderWallet,
order.sender.token,
order.sender.kind,
order.sender.id,
totalSenderAmount
);
if (senderWallet != address(0)) {
if (!senderTokenAdapter.hasAllowance(sender)) {
errors[count++] = "SenderAllowanceLow";
}
if (!senderTokenAdapter.hasBalance(sender)) {
errors[count++] = "SenderBalanceLow";
}
}
if (!senderTokenAdapter.hasValidParams(sender)) {
errors[count++] = "AmountOrIDInvalid";
}
if (order.sender.amount < order.affiliateAmount) {
errors[count++] = "AffiliateAmountInvalid";
}
}
}
IAdapter signerTokenAdapter = adapters[order.signer.kind];
if (address(signerTokenAdapter) == address(0)) {
errors[count++] = "SignerTokenKindUnknown";
} else {
if (!signerTokenAdapter.hasAllowance(order.signer)) {
errors[count++] = "SignerAllowanceLow";
}
if (!signerTokenAdapter.hasBalance(order.signer)) {
errors[count++] = "SignerBalanceLow";
}
if (!signerTokenAdapter.hasValidParams(order.signer)) {
errors[count++] = "AmountOrIDInvalid";
}
}
// Truncate errors array to actual count
if (count != errors.length) {
assembly {
mstore(errors, count)
}
}
return errors;
}
/**
* @notice Returns true if the nonce has been used
* @param signer address Address of the signer
* @param nonce uint256 Nonce being checked
*/
function nonceUsed(
address signer,
uint256 nonce
) public view override returns (bool) {
uint256 groupKey = nonce / 256;
uint256 indexInGroup = nonce % 256;
return (_nonceGroups[signer][groupKey] >> indexInGroup) & 1 == 1;
}
/**
* @notice Marks a nonce as used for the given signatory
* @param signatory address Address of the signer for which to mark the nonce as used
* @param nonce uint256 Nonce to be marked as used
*/
function _markNonceAsUsed(address signatory, uint256 nonce) private {
uint256 groupKey = nonce / 256;
uint256 indexInGroup = nonce % 256;
uint256 group = _nonceGroups[signatory][groupKey];
// Revert if nonce is already used
if ((group >> indexInGroup) & 1 == 1) {
revert NonceAlreadyUsed(nonce);
}
_nonceGroups[signatory][groupKey] = group | (uint256(1) << indexInGroup);
}
/**
* @notice Checks whether a token implements EIP-2981
* @param token address token to check
*/
function supportsRoyalties(address token) private view returns (bool) {
try IERC165(token).supportsInterface(type(IERC2981).interfaceId) returns (
bool result
) {
return result;
} catch {
return false;
}
}
/**
* @notice Tests whether signature and signer are valid
* @param order Order to validate
*/
function _check(Order calldata order) private {
// Ensure execution on the intended chain
if (DOMAIN_CHAIN_ID != block.chainid) revert ChainIdChanged();
// Ensure the sender token is the required kind
if (order.sender.kind != requiredSenderKind) revert SenderTokenInvalid();
// Ensure the sender amount is greater than affiliate amount
if (order.sender.amount < order.affiliateAmount)
revert AffiliateAmountInvalid();
// Validate as the authorized signatory if set
address signatory = order.signer.wallet;
if (authorized[signatory] != address(0)) {
signatory = authorized[signatory];
}
// Ensure the signature is correct for the order
if (
!SignatureChecker.isValidSignatureNow(
signatory,
_getOrderHash(order),
abi.encodePacked(order.r, order.s, order.v)
)
) revert Unauthorized();
// Ensure the nonce is not yet used and if not mark it used
_markNonceAsUsed(signatory, order.nonce);
// Ensure the nonce is not below the minimum nonce set by cancelUpTo
if (order.nonce < signatoryMinimumNonce[signatory]) revert NonceTooLow();
// Ensure the expiry is not passed
if (order.expiry <= block.timestamp) revert OrderExpired();
}
/**
* @notice Hashes an order into bytes32
* @dev EIP-191 header and domain separator included
* @param order Order The order to be hashed
* @return bytes32 A keccak256 abi.encodePacked value
*/
function _getOrderHash(Order calldata order) private view returns (bytes32) {
return
keccak256(
abi.encodePacked(
"\x19\x01", // EIP191: Indicates EIP712
DOMAIN_SEPARATOR,
keccak256(
abi.encode(
ORDER_TYPEHASH,
order.nonce,
order.expiry,
protocolFee,
keccak256(abi.encode(PARTY_TYPEHASH, order.signer)),
keccak256(abi.encode(PARTY_TYPEHASH, order.sender)),
order.affiliateWallet,
order.affiliateAmount
)
)
)
);
}
/**
* @notice Performs token transfer
* @param from address Wallet address to transfer from
* @param to address Wallet address to transfer to
* @param amount uint256 Amount for ERC-20
* @param id uint256 token ID for ERC-721, ERC-1155
* @param token address Contract address of token
* @param kind bytes4 EIP-165 interface ID of the token
*/
function _transfer(
address from,
address to,
uint256 amount,
uint256 id,
address token,
bytes4 kind
) private {
IAdapter adapter = adapters[kind];
if (address(adapter) == address(0)) revert TokenKindUnknown();
// Use delegatecall so underlying transfer is called as Swap
(bool success, ) = address(adapter).delegatecall(
abi.encodeWithSelector(
adapter.transfer.selector,
from,
to,
amount,
id,
token
)
);
if (!success) revert TransferFailed(from, to);
}
}
@openzeppelin/contracts/access/Ownable.sol
// 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);
}
}
@openzeppelin/contracts/access/Ownable2Step.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.0;
import "./Ownable.sol";
/**
* @dev Contract module which provides 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} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
}
@openzeppelin/contracts/interfaces/IERC1271.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*
* _Available since v4.1._
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
@openzeppelin/contracts/interfaces/IERC2981.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC2981.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/IERC165.sol";
/**
* @dev Interface for the NFT Royalty Standard.
*
* A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
* support for royalty payments across all NFT marketplaces and ecosystem participants.
*
* _Available since v4.5._
*/
interface IERC2981 is IERC165 {
/**
* @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
* exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
*/
function royaltyInfo(
uint256 tokenId,
uint256 salePrice
) external view returns (address receiver, uint256 royaltyAmount);
}
@openzeppelin/contracts/interfaces/IERC5267.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.0;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}
@openzeppelin/contracts/utils/Context.sol
// 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;
}
}
@openzeppelin/contracts/utils/ShortStrings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)
pragma solidity ^0.8.8;
import "./StorageSlot.sol";
// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
// | length | 0x BB |
type ShortString is bytes32;
/**
* @dev This library provides functions to convert short memory strings
* into a `ShortString` type that can be used as an immutable variable.
*
* Strings of arbitrary length can be optimized using this library if
* they are short enough (up to 31 bytes) by packing them with their
* length (1 byte) in a single EVM word (32 bytes). Additionally, a
* fallback mechanism can be used for every other case.
*
* Usage example:
*
* ```solidity
* contract Named {
* using ShortStrings for *;
*
* ShortString private immutable _name;
* string private _nameFallback;
*
* constructor(string memory contractName) {
* _name = contractName.toShortStringWithFallback(_nameFallback);
* }
*
* function name() external view returns (string memory) {
* return _name.toStringWithFallback(_nameFallback);
* }
* }
* ```
*/
library ShortStrings {
// Used as an identifier for strings longer than 31 bytes.
bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
error StringTooLong(string str);
error InvalidShortString();
/**
* @dev Encode a string of at most 31 chars into a `ShortString`.
*
* This will trigger a `StringTooLong` error is the input string is too long.
*/
function toShortString(string memory str) internal pure returns (ShortString) {
bytes memory bstr = bytes(str);
if (bstr.length > 31) {
revert StringTooLong(str);
}
return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
}
/**
* @dev Decode a `ShortString` back to a "normal" string.
*/
function toString(ShortString sstr) internal pure returns (string memory) {
uint256 len = byteLength(sstr);
// using `new string(len)` would work locally but is not memory safe.
string memory str = new string(32);
/// @solidity memory-safe-assembly
assembly {
mstore(str, len)
mstore(add(str, 0x20), sstr)
}
return str;
}
/**
* @dev Return the length of a `ShortString`.
*/
function byteLength(ShortString sstr) internal pure returns (uint256) {
uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
if (result > 31) {
revert InvalidShortString();
}
return result;
}
/**
* @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
*/
function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
if (bytes(value).length < 32) {
return toShortString(value);
} else {
StorageSlot.getStringSlot(store).value = value;
return ShortString.wrap(_FALLBACK_SENTINEL);
}
}
/**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*/
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
return toString(value);
} else {
return store;
}
}
/**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
*/
function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
return byteLength(value);
} else {
return bytes(store).length;
}
}
}
@openzeppelin/contracts/utils/StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
* _Available since v4.9 for `string`, `bytes`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}
@openzeppelin/contracts/utils/Strings.sol
// 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));
}
}
@openzeppelin/contracts/utils/cryptography/ECDSA.sol
// 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));
}
}
@openzeppelin/contracts/utils/cryptography/EIP712.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.8;
import "./ECDSA.sol";
import "../ShortStrings.sol";
import "../../interfaces/IERC5267.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
* separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
* separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
*
* _Available since v3.4._
*
* @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
*/
abstract contract EIP712 is IERC5267 {
using ShortStrings for *;
bytes32 private constant _TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _cachedDomainSeparator;
uint256 private immutable _cachedChainId;
address private immutable _cachedThis;
bytes32 private immutable _hashedName;
bytes32 private immutable _hashedVersion;
ShortString private immutable _name;
ShortString private immutable _version;
string private _nameFallback;
string private _versionFallback;
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
_name = name.toShortStringWithFallback(_nameFallback);
_version = version.toShortStringWithFallback(_versionFallback);
_hashedName = keccak256(bytes(name));
_hashedVersion = keccak256(bytes(version));
_cachedChainId = block.chainid;
_cachedDomainSeparator = _buildDomainSeparator();
_cachedThis = address(this);
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
return _cachedDomainSeparator;
} else {
return _buildDomainSeparator();
}
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev See {EIP-5267}.
*
* _Available since v4.9._
*/
function eip712Domain()
public
view
virtual
override
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
return (
hex"0f", // 01111
_name.toStringWithFallback(_nameFallback),
_version.toStringWithFallback(_versionFallback),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
}
@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/SignatureChecker.sol)
pragma solidity ^0.8.0;
import "./ECDSA.sol";
import "../../interfaces/IERC1271.sol";
/**
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
* Argent and Gnosis Safe.
*
* _Available since v4.1._
*/
library SignatureChecker {
/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
* signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
(address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature);
return
(error == ECDSA.RecoverError.NoError && recovered == signer) ||
isValidERC1271SignatureNow(signer, hash, signature);
}
/**
* @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
* against the signer smart contract using ERC1271.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidERC1271SignatureNow(
address signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
(bool success, bytes memory result) = signer.staticcall(
abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature)
);
return (success &&
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
}
}
@openzeppelin/contracts/utils/introspection/IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
@openzeppelin/contracts/utils/math/Math.sol
// 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);
}
}
}
@openzeppelin/contracts/utils/math/SignedMath.sol
// 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);
}
}
}
contracts/interfaces/IAdapter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
struct Party {
address wallet; // Wallet address of the party
address token; // Contract address of the token
bytes4 kind; // Interface ID of the token
uint256 id; // ID for ERC-721 or ERC-1155
uint256 amount; // Amount for ERC-20 or ERC-1155
}
/**
* @title IAdapter: Adapter for various token kinds
*/
interface IAdapter {
/**
* @notice Revert if provided an invalid transfer argument
*/
error AmountOrIDInvalid(string);
/**
* @notice Return the ERC165 interfaceId this adapter supports
*/
function interfaceId() external view returns (bytes4);
/**
* @notice Checks allowance on a token
* @param party Party params to check
*/
function hasAllowance(Party calldata party) external view returns (bool);
/**
* @notice Checks balance on a token
* @param party Party params to check
*/
function hasBalance(Party calldata party) external view returns (bool);
/**
* @notice Checks params for transfer
* @param party Party params to check
*/
function hasValidParams(Party calldata party) external view returns (bool);
/**
* @notice Function to wrap token transfer for different token types
* @param from address Wallet address to transfer from
* @param to address Wallet address to transfer to
* @param amount uint256 Amount for ERC-20
* @param id token ID for ERC-721
* @param token address Contract address of token
*/
function transfer(
address from,
address to,
uint256 amount,
uint256 id,
address token
) external;
}
contracts/interfaces/ISwap.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "./IAdapter.sol";
interface ISwap {
struct Order {
uint256 nonce; // Unique number per signatory per order
uint256 expiry; // Expiry time (seconds since unix epoch)
Party signer; // Party to the swap that sets terms
Party sender; // Party to the swap that accepts terms
address affiliateWallet; // Party tipped for facilitating (optional)
uint256 affiliateAmount;
uint8 v; // ECDSA
bytes32 r;
bytes32 s;
}
event Swap(
uint256 indexed nonce,
address indexed signerWallet,
uint256 signerAmount,
uint256 signerId,
address signerToken,
address indexed senderWallet,
uint256 senderAmount,
uint256 senderId,
address senderToken,
address affiliateWallet,
uint256 affiliateAmount
);
event Cancel(uint256 indexed nonce, address indexed signerWallet);
event CancelUpTo(uint256 indexed nonce, address indexed signerWallet);
event SetProtocolFee(uint256 protocolFee);
event SetProtocolFeeWallet(address indexed feeWallet);
event Authorize(address indexed signer, address indexed signerWallet);
event Revoke(address indexed signer, address indexed signerWallet);
error ChainIdChanged();
error AdaptersInvalid();
error FeeInvalid();
error FeeWalletInvalid();
error NonceAlreadyUsed(uint256);
error NonceTooLow();
error OrderExpired();
error SenderInvalid();
error SenderTokenInvalid();
error AffiliateAmountInvalid();
error SignatureInvalid();
error SignatoryInvalid();
error RoyaltyExceedsMax(uint256);
error TokenKindUnknown();
error TransferFailed(address, address);
error SignatoryUnauthorized();
error Unauthorized();
function swap(
address recipient,
uint256 maxRoyalty,
Order calldata order
) external;
function cancel(uint256[] calldata nonces) external;
function cancelUpTo(uint256 minimumNonce) external;
function check(
address,
Order calldata
) external view returns (bytes32[] memory);
function nonceUsed(address, uint256) external view returns (bool);
function authorize(address sender) external;
function revoke() external;
function adapters(bytes4) external view returns (IAdapter);
function authorized(address) external view returns (address);
function signatoryMinimumNonce(address) external view returns (uint256);
}
Compiler Settings
{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":999999,"enabled":true},"libraries":{},"evmVersion":"paris"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address[]","name":"_adapters","internalType":"contract IAdapter[]"},{"type":"bytes4","name":"_requiredSenderKind","internalType":"bytes4"},{"type":"uint256","name":"_protocolFee","internalType":"uint256"},{"type":"address","name":"_protocolFeeWallet","internalType":"address"}]},{"type":"error","name":"AdaptersInvalid","inputs":[]},{"type":"error","name":"AffiliateAmountInvalid","inputs":[]},{"type":"error","name":"ChainIdChanged","inputs":[]},{"type":"error","name":"FeeInvalid","inputs":[]},{"type":"error","name":"FeeWalletInvalid","inputs":[]},{"type":"error","name":"InvalidShortString","inputs":[]},{"type":"error","name":"NonceAlreadyUsed","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"error","name":"NonceTooLow","inputs":[]},{"type":"error","name":"OrderExpired","inputs":[]},{"type":"error","name":"RoyaltyExceedsMax","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"error","name":"SenderInvalid","inputs":[]},{"type":"error","name":"SenderTokenInvalid","inputs":[]},{"type":"error","name":"SignatoryInvalid","inputs":[]},{"type":"error","name":"SignatoryUnauthorized","inputs":[]},{"type":"error","name":"SignatureInvalid","inputs":[]},{"type":"error","name":"StringTooLong","inputs":[{"type":"string","name":"str","internalType":"string"}]},{"type":"error","name":"TokenKindUnknown","inputs":[]},{"type":"error","name":"TransferFailed","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"error","name":"Unauthorized","inputs":[]},{"type":"event","name":"Authorize","inputs":[{"type":"address","name":"signer","internalType":"address","indexed":true},{"type":"address","name":"signerWallet","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Cancel","inputs":[{"type":"uint256","name":"nonce","internalType":"uint256","indexed":true},{"type":"address","name":"signerWallet","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"CancelUpTo","inputs":[{"type":"uint256","name":"nonce","internalType":"uint256","indexed":true},{"type":"address","name":"signerWallet","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"EIP712DomainChanged","inputs":[],"anonymous":false},{"type":"event","name":"OwnershipTransferStarted","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Revoke","inputs":[{"type":"address","name":"signer","internalType":"address","indexed":true},{"type":"address","name":"signerWallet","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"SetProtocolFee","inputs":[{"type":"uint256","name":"protocolFee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetProtocolFeeWallet","inputs":[{"type":"address","name":"feeWallet","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Swap","inputs":[{"type":"uint256","name":"nonce","internalType":"uint256","indexed":true},{"type":"address","name":"signerWallet","internalType":"address","indexed":true},{"type":"uint256","name":"signerAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"signerId","internalType":"uint256","indexed":false},{"type":"address","name":"signerToken","internalType":"address","indexed":false},{"type":"address","name":"senderWallet","internalType":"address","indexed":true},{"type":"uint256","name":"senderAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"senderId","internalType":"uint256","indexed":false},{"type":"address","name":"senderToken","internalType":"address","indexed":false},{"type":"address","name":"affiliateWallet","internalType":"address","indexed":false},{"type":"uint256","name":"affiliateAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"DOMAIN_CHAIN_ID","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"DOMAIN_NAME","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DOMAIN_SEPARATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"DOMAIN_VERSION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"FEE_DIVISOR","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"acceptOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IAdapter"}],"name":"adapters","inputs":[{"type":"bytes4","name":"","internalType":"bytes4"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"authorize","inputs":[{"type":"address","name":"signatory","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"authorized","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancel","inputs":[{"type":"uint256[]","name":"nonces","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelUpTo","inputs":[{"type":"uint256","name":"minimumNonce","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32[]","name":"","internalType":"bytes32[]"}],"name":"check","inputs":[{"type":"address","name":"senderWallet","internalType":"address"},{"type":"tuple","name":"order","internalType":"struct ISwap.Order","components":[{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"uint256","name":"expiry","internalType":"uint256"},{"type":"tuple","name":"signer","internalType":"struct Party","components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"address","name":"token","internalType":"address"},{"type":"bytes4","name":"kind","internalType":"bytes4"},{"type":"uint256","name":"id","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"tuple","name":"sender","internalType":"struct Party","components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"address","name":"token","internalType":"address"},{"type":"bytes4","name":"kind","internalType":"bytes4"},{"type":"uint256","name":"id","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"address","name":"affiliateWallet","internalType":"address"},{"type":"uint256","name":"affiliateAmount","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes1","name":"fields","internalType":"bytes1"},{"type":"string","name":"name","internalType":"string"},{"type":"string","name":"version","internalType":"string"},{"type":"uint256","name":"chainId","internalType":"uint256"},{"type":"address","name":"verifyingContract","internalType":"address"},{"type":"bytes32","name":"salt","internalType":"bytes32"},{"type":"uint256[]","name":"extensions","internalType":"uint256[]"}],"name":"eip712Domain","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"nonceUsed","inputs":[{"type":"address","name":"signer","internalType":"address"},{"type":"uint256","name":"nonce","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"pendingOwner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"protocolFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"protocolFeeWallet","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes4","name":"","internalType":"bytes4"}],"name":"requiredSenderKind","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revoke","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setProtocolFee","inputs":[{"type":"uint256","name":"_protocolFee","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setProtocolFeeWallet","inputs":[{"type":"address","name":"_protocolFeeWallet","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"signatoryMinimumNonce","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"swap","inputs":[{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"maxRoyalty","internalType":"uint256"},{"type":"tuple","name":"order","internalType":"struct ISwap.Order","components":[{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"uint256","name":"expiry","internalType":"uint256"},{"type":"tuple","name":"signer","internalType":"struct Party","components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"address","name":"token","internalType":"address"},{"type":"bytes4","name":"kind","internalType":"bytes4"},{"type":"uint256","name":"id","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"tuple","name":"sender","internalType":"struct Party","components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"address","name":"token","internalType":"address"},{"type":"bytes4","name":"kind","internalType":"bytes4"},{"type":"uint256","name":"id","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"address","name":"affiliateWallet","internalType":"address"},{"type":"uint256","name":"affiliateAmount","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]}]
Contract Creation Code
0x6101c06040523480156200001257600080fd5b5060405162003b1138038062003b118339810160408190526200003591620004b3565b604051806040016040528060048152602001630535741560e41b815250604051806040016040528060038152602001621a171960e91b8152506200008862000082620002da60201b60201c565b620002de565b62000095826002620002fc565b61012052620000a6816003620002fc565b61014052815160208084019190912060e052815190820120610100524660a0526200012360e051610100516040805160008051602062003af183398151915260208201529081019290925260608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60805250503060c05261271082106200014f576040516352dadcf960e01b815260040160405180910390fd5b6001600160a01b0381166200017757604051636b8df36360e01b815260040160405180910390fd5b83516000036200019a57604051632cb13a8760e21b815260040160405180910390fd5b4661016052620001a962000335565b61018052835160005b818110156200029e57858181518110620001d057620001d0620005b5565b602002602001015160096000888481518110620001f157620001f1620005b5565b60200260200101516001600160a01b031663a64d0cd46040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000237573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200025d9190620005cb565b6001600160e01b0319168152602081019190915260400160002080546001600160a01b0319166001600160a01b0392909216919091179055600101620001b2565b50506001600160e01b0319929092166101a052600755600880546001600160a01b0319166001600160a01b0390921691909117905550620007c3565b3390565b600180546001600160a01b0319169055620002f981620003cb565b50565b60006020835110156200031c5762000314836200041b565b90506200032f565b8162000329848262000681565b5060ff90505b92915050565b600060c0516001600160a01b0316306001600160a01b03161480156200035c575060a05146145b1562000369575060805190565b620003c660e051610100516040805160008051602062003af183398151915260208201529081019290925260608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b905090565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600080829050601f8151111562000452578260405163305a27a960e01b81526004016200044991906200074d565b60405180910390fd5b80516200045f826200079e565b179392505050565b634e487b7160e01b600052604160045260246000fd5b80516001600160a01b03811681146200049557600080fd5b919050565b80516001600160e01b0319811681146200049557600080fd5b60008060008060808587031215620004ca57600080fd5b84516001600160401b0380821115620004e257600080fd5b818701915087601f830112620004f757600080fd5b81516020828211156200050e576200050e62000467565b8160051b604051601f19603f8301168101818110868211171562000536576200053662000467565b60405292835281830193508481018201928b8411156200055557600080fd5b948201945b838610156200057e576200056e866200047d565b855294820194938201936200055a565b98506200058f90508982016200049a565b96505050505060408501519150620005aa606086016200047d565b905092959194509250565b634e487b7160e01b600052603260045260246000fd5b600060208284031215620005de57600080fd5b620005e9826200049a565b9392505050565b600181811c908216806200060557607f821691505b6020821081036200062657634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200067c576000816000526020600020601f850160051c81016020861015620006575750805b601f850160051c820191505b81811015620006785782815560010162000663565b5050505b505050565b81516001600160401b038111156200069d576200069d62000467565b620006b581620006ae8454620005f0565b846200062c565b602080601f831160018114620006ed5760008415620006d45750858301515b600019600386901b1c1916600185901b17855562000678565b600085815260208120601f198616915b828110156200071e57888601518255948401946001909101908401620006fd565b50858210156200073d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020808352835180602085015260005b818110156200077d578581018301518582016040015282016200075f565b506000604082860101526040601f19601f8301168501019250505092915050565b80516020808301519190811015620006265760001960209190910360031b1b16919050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a05161329e62000853600039600081816101b801528181610ee70152611ea501526000818161024d015261239801526000818161029501528181610a8c0152611e2c01526000611af201526000611ac70152600050506000505060005050600050506000505061329e6000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c80637ce78525116100ee578063b0e21e8a11610097578063b918161111610071578063b918161114610486578063cbf7c6c3146104bc578063e30c3978146104dc578063f2fde38b146104fa57600080fd5b8063b0e21e8a14610462578063b6549f751461046b578063b6a5d7de1461047357600080fd5b80639e93ad8e116100c85780639e93ad8e146103fd578063acb8cc4914610406578063b0dd49431461044257600080fd5b80637ce78525146103b157806384b0196e146103c45780638da5cb5b146103df57600080fd5b8063485421051161015b578063715018a611610135578063715018a614610345578063787dce3d1461034d578063796f077b1461036057806379ba5097146103a957600080fd5b806348542105146102b757806349b8a932146102ca5780636e038752146102ea57600080fd5b80633644e5151161018c5780633644e515146102485780633c6419101461027d578063416f281d1461029057600080fd5b806306a6ea74146101b35780631647795e146102105780632e34082314610233575b600080fd5b6101da7f000000000000000000000000000000000000000000000000000000000000000081565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020015b60405180910390f35b61022361021e366004612c2f565b61050d565b6040519015158152602001610207565b610246610241366004612c5b565b610572565b005b61026f7f000000000000000000000000000000000000000000000000000000000000000081565b604051908152602001610207565b61024661028b366004612cd0565b6105df565b61026f7f000000000000000000000000000000000000000000000000000000000000000081565b6102466102c5366004612d02565b61061b565b6102dd6102d8366004612d42565b610a5e565b6040516102079190612d79565b6103206102f8366004612ded565b60096020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610207565b6102466118ae565b61024661035b366004612cd0565b6118c2565b61039c6040518060400160405280600481526020017f535741500000000000000000000000000000000000000000000000000000000081525081565b6040516102079190612e76565b610246611940565b6102466103bf366004612e89565b6119f5565b6103cc611ab9565b6040516102079796959493929190612ea6565b60005473ffffffffffffffffffffffffffffffffffffffff16610320565b61026f61271081565b61039c6040518060400160405280600381526020017f342e32000000000000000000000000000000000000000000000000000000000081525081565b61026f610450366004612e89565b60066020526000908152604090205481565b61026f60075481565b610246611b5e565b610246610481366004612e89565b611bdb565b610320610494366004612e89565b60056020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6008546103209073ffffffffffffffffffffffffffffffffffffffff1681565b60015473ffffffffffffffffffffffffffffffffffffffff16610320565b610246610508366004612e89565b611ca3565b60008061051c61010084612fc6565b9050600061052c61010085612fda565b73ffffffffffffffffffffffffffffffffffffffff8616600090815260046020908152604080832095835294905292909220546001921c82169091149150505b92915050565b60005b818110156105da57600083838381811061059157610591612fee565b9050602002013590506105a43382611d53565b604051339082907f8dd3c361eb2366ff27c2db0eb07b9261f1d052570742ab8c9a0c326f37aa576d90600090a350600101610575565b505050565b336000818152600660205260408082208490555183917f863123978d9b13946753a916c935c0688a01802440d3ffc668d04d2720c4e11091a350565b61062481611e29565b6000610637610100830160e08401612e89565b73ffffffffffffffffffffffffffffffffffffffff1614158015610681575033610668610100830160e08401612e89565b73ffffffffffffffffffffffffffffffffffffffff1614155b156106b8576040517fa7202ef600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610701336106cc6060840160408501612e89565b6101608401356101408501356106ea61012087016101008801612e89565b6106fc61014088016101208901612ded565b6120eb565b61073f6107146060830160408401612e89565b8460c084013560a085013561072f6080870160608801612e89565b6106fc60a0880160808901612ded565b60006107536101a083016101808401612e89565b73ffffffffffffffffffffffffffffffffffffffff16146107a2576107a2336107846101a084016101808501612e89565b6101a08401356101408501356106ea61012087016101008801612e89565b600754600090612710906107bb9061016085013561301d565b6107c59190612fc6565b905080156108175760085461081790339073ffffffffffffffffffffffffffffffffffffffff168361014086013561080561012088016101008901612e89565b6106fc61014089016101208a01612ded565b61082f61082a6080840160608501612e89565b6122bc565b15610965576000806108476080850160608601612e89565b6040517f2a55205a00000000000000000000000000000000000000000000000000000000815260a08601356004820152610160860135602482015273ffffffffffffffffffffffffffffffffffffffff9190911690632a55205a906044016040805180830381865afa1580156108c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e59190613034565b909250905080156109625784811115610932576040517f9c08743a000000000000000000000000000000000000000000000000000000008152600481018290526024015b60405180910390fd5b6109623383836101408801356109506101208a016101008b01612e89565b6106fc6101408b016101208c01612ded565b50505b336109766060840160408501612e89565b73ffffffffffffffffffffffffffffffffffffffff1683357f182e847dc18073123e8aa17e204b9e3874caf71387e52fe3083ffb98716d3d6b60c086013560a08701356109c96080890160608a01612e89565b6101608901356101408a01356109e76101208c016101008d01612e89565b6109f96101a08d016101808e01612e89565b60408051978852602088019690965273ffffffffffffffffffffffffffffffffffffffff9485169587019590955260608601929092526080850152811660a08401521660c08201526101a087013560e08201526101000160405180910390a450505050565b60408051601080825261022082019092526060916000919060208201610200803683370190505090506000467f000000000000000000000000000000000000000000000000000000000000000014610afb577f436861696e49644368616e6765640000000000000000000000000000000000008282610adc81613062565b935081518110610aee57610aee612fee565b6020026020010181815250505b6000610b0d6060860160408701612e89565b73ffffffffffffffffffffffffffffffffffffffff8082166000908152600560205260409020549192501615610b665773ffffffffffffffffffffffffffffffffffffffff908116600090815260056020526040902054165b610bec81610b7387612394565b6101e088018035906102008a013590610b90906101c08c0161309a565b604051602001610bd893929190928352602083019190915260f81b7fff0000000000000000000000000000000000000000000000000000000000000016604082015260410190565b6040516020818303038152906040526126ad565b610c3f577f556e617574686f72697a656400000000000000000000000000000000000000008383610c1c81613062565b945081518110610c2e57610c2e612fee565b602002602001018181525050610cf4565b610c4a81863561050d565b15610c7b577f4e6f6e6365416c726561647955736564000000000000000000000000000000008383610c1c81613062565b73ffffffffffffffffffffffffffffffffffffffff811660009081526006602052604090205485351015610cf4577f4e6f6e6365546f6f4c6f770000000000000000000000000000000000000000008383610cd581613062565b945081518110610ce757610ce7612fee565b6020026020010181815250505b4285602001351015610d4b577f4f726465724578706972656400000000000000000000000000000000000000008383610d2c81613062565b945081518110610d3e57610d3e612fee565b6020026020010181815250505b6000610d5e610100870160e08801612e89565b73ffffffffffffffffffffffffffffffffffffffff1614158015610dbe575073ffffffffffffffffffffffffffffffffffffffff8616610da5610100870160e08801612e89565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610e0e577f53656e646572496e76616c6964000000000000000000000000000000000000008383610def81613062565b945081518110610e0157610e01612fee565b6020026020010181815250505b6000600981610e2561014089016101208a01612ded565b7fffffffff0000000000000000000000000000000000000000000000000000000016815260208101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff16905080610ec4577f53656e646572546f6b656e4b696e64556e6b6e6f776e000000000000000000008484610ea181613062565b955081518110610eb357610eb3612fee565b602002602001018181525050611536565b7fffffffff000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000016610f1961014088016101208901612ded565b7fffffffff000000000000000000000000000000000000000000000000000000001614610f6c577f53656e646572546f6b656e496e76616c696400000000000000000000000000008484610ea181613062565b60075460009061271090610f85906101608a013561301d565b610f8f9190612fc6565b905060006101a0880135610fa8836101608b01356130bd565b610fb291906130bd565b9050610fc761082a60808a0160608b01612e89565b1561108e576000610fde60808a0160608b01612e89565b6040517f2a55205a00000000000000000000000000000000000000000000000000000000815260a08b013560048201526101608b0135602482015273ffffffffffffffffffffffffffffffffffffffff9190911690632a55205a906044016040805180830381865afa158015611058573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061107c9190613034565b915061108a905081836130bd565b9150505b6040805160a0810190915273ffffffffffffffffffffffffffffffffffffffff8a168152600090602081016110cb6101208c016101008d01612e89565b73ffffffffffffffffffffffffffffffffffffffff1681526020016110f86101408c016101208d01612ded565b7fffffffff000000000000000000000000000000000000000000000000000000001681526101408b01356020820152604001839052905073ffffffffffffffffffffffffffffffffffffffff8a16156113a757604080517f3170f63d000000000000000000000000000000000000000000000000000000008152825173ffffffffffffffffffffffffffffffffffffffff9081166004830152602084015181166024830152918301517fffffffff00000000000000000000000000000000000000000000000000000000166044820152606083015160648201526080830151608482015290851690633170f63d9060a401602060405180830381865afa158015611206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061122a91906130d0565b611279577f53656e646572416c6c6f77616e63654c6f770000000000000000000000000000878761125a81613062565b98508151811061126c5761126c612fee565b6020026020010181815250505b604080517fd1190df9000000000000000000000000000000000000000000000000000000008152825173ffffffffffffffffffffffffffffffffffffffff9081166004830152602084015181166024830152918301517fffffffff0000000000000000000000000000000000000000000000000000000016604482015260608301516064820152608083015160848201529085169063d1190df99060a401602060405180830381865afa158015611334573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061135891906130d0565b6113a7577f53656e64657242616c616e63654c6f7700000000000000000000000000000000878761138881613062565b98508151811061139a5761139a612fee565b6020026020010181815250505b604080517f40944de9000000000000000000000000000000000000000000000000000000008152825173ffffffffffffffffffffffffffffffffffffffff9081166004830152602084015181166024830152918301517fffffffff000000000000000000000000000000000000000000000000000000001660448201526060830151606482015260808301516084820152908516906340944de99060a401602060405180830381865afa158015611462573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061148691906130d0565b6114d5577f416d6f756e744f724944496e76616c696400000000000000000000000000000087876114b681613062565b9850815181106114c8576114c8612fee565b6020026020010181815250505b6101a08901356101608a01351015611532577f416666696c69617465416d6f756e74496e76616c696400000000000000000000878761151381613062565b98508151811061152557611525612fee565b6020026020010181815250505b5050505b600060098161154b60a08a0160808b01612ded565b7fffffffff0000000000000000000000000000000000000000000000000000000016815260208101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff169050806115ea577f5369676e6572546f6b656e4b696e64556e6b6e6f776e0000000000000000000085856115c781613062565b9650815181106115d9576115d9612fee565b602002602001018181525050611896565b604080517f3170f63d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831691633170f63d9161163e918b0190600401613174565b602060405180830381865afa15801561165b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167f91906130d0565b6116ce577f5369676e6572416c6c6f77616e63654c6f77000000000000000000000000000085856116af81613062565b9650815181106116c1576116c1612fee565b6020026020010181815250505b604080517fd1190df900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83169163d1190df991611722918b0190600401613174565b602060405180830381865afa15801561173f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176391906130d0565b6117b2577f5369676e657242616c616e63654c6f7700000000000000000000000000000000858561179381613062565b9650815181106117a5576117a5612fee565b6020026020010181815250505b604080517f40944de900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316916340944de991611806918b0190600401613174565b602060405180830381865afa158015611823573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061184791906130d0565b611896577f416d6f756e744f724944496e76616c6964000000000000000000000000000000858561187781613062565b96508151811061188957611889612fee565b6020026020010181815250505b845184146118a2578385525b50929695505050505050565b6118b661272a565b6118c060006127ab565b565b6118ca61272a565b6127108110611905576040517f52dadcf900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60078190556040518181527fdc0410a296e1e33943a772020d333d5f99319d7fcad932a484c53889f7aaa2b19060200160405180910390a150565b600154339073ffffffffffffffffffffffffffffffffffffffff1681146119e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f74207468652060448201527f6e6577206f776e657200000000000000000000000000000000000000000000006064820152608401610929565b6119f2816127ab565b50565b6119fd61272a565b73ffffffffffffffffffffffffffffffffffffffff8116611a4a576040517f6b8df36300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f8b2a800ce9e2e7ccdf4741ae0e41b1f16983192291080ae3b78ac4296ddf598a90600090a250565b600060608082808083611aed7f000000000000000000000000000000000000000000000000000000000000000060026127dc565b611b187f000000000000000000000000000000000000000000000000000000000000000060036127dc565b604080516000808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009b939a50919850469750309650945092509050565b3360008181526005602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000008116909155905173ffffffffffffffffffffffffffffffffffffffff909116929183917fd7426110292f20fe59e73ccf52124e0f5440a756507c91c7b0a6c50e1eb1a23a9190a350565b73ffffffffffffffffffffffffffffffffffffffff8116611c28576040517fcd4b78cf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360008181526005602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8616908117909155905190917f30468de898bda644e26bab66e5a2241a3aa6aaf527257f5ca54e0f65204ba14a91a350565b611cab61272a565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff00000000000000000000000000000000000000009091168117909155611d0e60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6000611d6161010083612fc6565b90506000611d7161010084612fda565b73ffffffffffffffffffffffffffffffffffffffff85166000908152600460209081526040808320868452909152902054909150600181831c81169003611de7576040517f91cab50400000000000000000000000000000000000000000000000000000000815260048101859052602401610929565b73ffffffffffffffffffffffffffffffffffffffff909416600090815260046020908152604080832094835293905291909120600190911b9290921790915550565b467f000000000000000000000000000000000000000000000000000000000000000014611e82576040517fc614eff800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffff000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000016611ed761014083016101208401612ded565b7fffffffff000000000000000000000000000000000000000000000000000000001614611f30576040517fd0bd721a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101a08101356101608201351015611f74576040517f3b71bbca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611f866060830160408401612e89565b73ffffffffffffffffffffffffffffffffffffffff8082166000908152600560205260409020549192501615611fdf5773ffffffffffffffffffffffffffffffffffffffff908116600090815260056020526040902054165b61200981611fec84612394565b6101e0850180359061020087013590610b90906101c0890161309a565b61203f576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61204a818335611d53565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260066020526040902054823510156120aa576040517fd24d82a400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b428260200135116120e7576040517fc56873ba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b7fffffffff00000000000000000000000000000000000000000000000000000000811660009081526009602052604090205473ffffffffffffffffffffffffffffffffffffffff168061216a576040517f33c8207c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805173ffffffffffffffffffffffffffffffffffffffff89811660248301528881166044830152606482018890526084820187905285811660a4808401919091528351808403909101815260c490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f47338bc300000000000000000000000000000000000000000000000000000000179052915160009284169161221791613182565b600060405180830381855af49150503d8060008114612252576040519150601f19603f3d011682016040523d82523d6000602084013e612257565b606091505b50509050806122b2576040517f523c3aaa00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808a16600483015288166024820152604401610929565b5050505050505050565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f2a55205a00000000000000000000000000000000000000000000000000000000600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906301ffc9a790602401602060405180830381865afa925050508015612383575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252612380918101906130d0565b60015b61056c57506000919050565b919050565b60007f00000000000000000000000000000000000000000000000000000000000000006040516020016124f4907f4f726465722875696e74323536206e6f6e63652c75696e74323536206578706981527f72792c75696e743235362070726f746f636f6c4665652c50617274792073696760208201527f6e65722c50617274792073656e6465722c6164647265737320616666696c696160408201527f746557616c6c65742c75696e7432353620616666696c69617465416d6f756e7460608201527f290000000000000000000000000000000000000000000000000000000000000060808201527f506172747928616464726573732077616c6c65742c6164647265737320746f6b60818201527f656e2c627974657334206b696e642c75696e743235362069642c75696e74323560a18201527f3620616d6f756e7429000000000000000000000000000000000000000000000060c182015260ca0190565b60405160208183030381529060405280519060200120836000013584602001356007547f224ed05dccb4f5c1e21e6e85fb29dd6c83074e33458fdfefb184d03fe8742f4f8760400160405160200161254d92919061319e565b604051602081830303815290604052805190602001207f224ed05dccb4f5c1e21e6e85fb29dd6c83074e33458fdfefb184d03fe8742f4f8860e00160405160200161259992919061319e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101206125e16101a08a016101808b01612e89565b6040805160208101989098528701959095526060860193909352608085019190915260a084015260c083015273ffffffffffffffffffffffffffffffffffffffff1660e08201526101a084013561010082015261012001604051602081830303815290604052805190602001206040516020016126909291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604051602081830303815290604052805190602001209050919050565b60008060006126bc8585612887565b909250905060008160048111156126d5576126d56131b2565b14801561270d57508573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b8061271e575061271e8686866128cc565b925050505b9392505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146118c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610929565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556119f281612a29565b606060ff83146127f6576127ef83612a9e565b905061056c565b818054612802906131e1565b80601f016020809104026020016040519081016040528092919081815260200182805461282e906131e1565b801561287b5780601f106128505761010080835404028352916020019161287b565b820191906000526020600020905b81548152906001019060200180831161285e57829003601f168201915b5050505050905061056c565b60008082516041036128bd5760208301516040840151606085015160001a6128b187828585612add565b945094505050506128c5565b506000905060025b9250929050565b60008060008573ffffffffffffffffffffffffffffffffffffffff16631626ba7e60e01b868660405160240161290392919061322e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161298c9190613182565b600060405180830381855afa9150503d80600081146129c7576040519150601f19603f3d011682016040523d82523d6000602084013e6129cc565b606091505b50915091508180156129e057506020815110155b801561271e575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090612a1e908301602090810190840161324f565b149695505050505050565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60606000612aab83612bcc565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115612b145750600090506003612bc3565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015612b68573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff8116612bbc57600060019250925050612bc3565b9150600090505b94509492505050565b600060ff8216601f81111561056c576040517fb3512b0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811681146119f257600080fd5b60008060408385031215612c4257600080fd5b8235612c4d81612c0d565b946020939093013593505050565b60008060208385031215612c6e57600080fd5b823567ffffffffffffffff80821115612c8657600080fd5b818501915085601f830112612c9a57600080fd5b813581811115612ca957600080fd5b8660208260051b8501011115612cbe57600080fd5b60209290920196919550909350505050565b600060208284031215612ce257600080fd5b5035919050565b60006102208284031215612cfc57600080fd5b50919050565b60008060006102608486031215612d1857600080fd5b8335612d2381612c0d565b925060208401359150612d398560408601612ce9565b90509250925092565b6000806102408385031215612d5657600080fd5b8235612d6181612c0d565b9150612d708460208501612ce9565b90509250929050565b6020808252825182820181905260009190848201906040850190845b81811015612db157835183529284019291840191600101612d95565b50909695505050505050565b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461238f57600080fd5b600060208284031215612dff57600080fd5b61272382612dbd565b60005b83811015612e23578181015183820152602001612e0b565b50506000910152565b60008151808452612e44816020860160208601612e08565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006127236020830184612e2c565b600060208284031215612e9b57600080fd5b813561272381612c0d565b7fff00000000000000000000000000000000000000000000000000000000000000881681526000602060e06020840152612ee360e084018a612e2c565b8381036040850152612ef5818a612e2c565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c08601528551808252602080880193509091019060005b81811015612f5657835183529284019291840191600101612f3a565b50909c9b505050505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082612fd557612fd5612f68565b500490565b600082612fe957612fe9612f68565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b808202811582820484141761056c5761056c612f97565b6000806040838503121561304757600080fd5b825161305281612c0d565b6020939093015192949293505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361309357613093612f97565b5060010190565b6000602082840312156130ac57600080fd5b813560ff8116811461272357600080fd5b8082018082111561056c5761056c612f97565b6000602082840312156130e257600080fd5b8151801515811461272357600080fd5b80356130fd81612c0d565b73ffffffffffffffffffffffffffffffffffffffff908116835260208201359061312682612c0d565b1660208301527fffffffff0000000000000000000000000000000000000000000000000000000061315960408301612dbd565b16604083015260608181013590830152608090810135910152565b60a0810161056c82846130f2565b60008251613194818460208701612e08565b9190910192915050565b82815260c0810161272360208301846130f2565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600181811c908216806131f557607f821691505b602082108103612cfc577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b8281526040602082015260006132476040830184612e2c565b949350505050565b60006020828403121561326157600080fd5b505191905056fea2646970667358221220b1ca1e33580bf8fa9028f5451f5caaaa05116d8c26193b7169108ad944e789e364736f6c634300081700338b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f000000000000000000000000000000000000000000000000000000000000008036372b07000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032000000000000000000000000c99c045f61cf8f628314170764a27cdeb75d2e2800000000000000000000000000000000000000000000000000000000000000030000000000000000000000002925817ae9be8251e6b281bdb1fad175c671c32c0000000000000000000000003b07b2bb728d0bf1271c19c22a997d8407e9aa950000000000000000000000008720c4573ab7e89c9e26951c49f5b80b8429e016
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106101ae5760003560e01c80637ce78525116100ee578063b0e21e8a11610097578063b918161111610071578063b918161114610486578063cbf7c6c3146104bc578063e30c3978146104dc578063f2fde38b146104fa57600080fd5b8063b0e21e8a14610462578063b6549f751461046b578063b6a5d7de1461047357600080fd5b80639e93ad8e116100c85780639e93ad8e146103fd578063acb8cc4914610406578063b0dd49431461044257600080fd5b80637ce78525146103b157806384b0196e146103c45780638da5cb5b146103df57600080fd5b8063485421051161015b578063715018a611610135578063715018a614610345578063787dce3d1461034d578063796f077b1461036057806379ba5097146103a957600080fd5b806348542105146102b757806349b8a932146102ca5780636e038752146102ea57600080fd5b80633644e5151161018c5780633644e515146102485780633c6419101461027d578063416f281d1461029057600080fd5b806306a6ea74146101b35780631647795e146102105780632e34082314610233575b600080fd5b6101da7f36372b070000000000000000000000000000000000000000000000000000000081565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020015b60405180910390f35b61022361021e366004612c2f565b61050d565b6040519015158152602001610207565b610246610241366004612c5b565b610572565b005b61026f7f8c466c1040f056c2de0a3a0961d6eccf8c51d50d91f69d55f4cdc6ca3c528f6d81565b604051908152602001610207565b61024661028b366004612cd0565b6105df565b61026f7f00000000000000000000000000000000000000000000000000000000000003af81565b6102466102c5366004612d02565b61061b565b6102dd6102d8366004612d42565b610a5e565b6040516102079190612d79565b6103206102f8366004612ded565b60096020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610207565b6102466118ae565b61024661035b366004612cd0565b6118c2565b61039c6040518060400160405280600481526020017f535741500000000000000000000000000000000000000000000000000000000081525081565b6040516102079190612e76565b610246611940565b6102466103bf366004612e89565b6119f5565b6103cc611ab9565b6040516102079796959493929190612ea6565b60005473ffffffffffffffffffffffffffffffffffffffff16610320565b61026f61271081565b61039c6040518060400160405280600381526020017f342e32000000000000000000000000000000000000000000000000000000000081525081565b61026f610450366004612e89565b60066020526000908152604090205481565b61026f60075481565b610246611b5e565b610246610481366004612e89565b611bdb565b610320610494366004612e89565b60056020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6008546103209073ffffffffffffffffffffffffffffffffffffffff1681565b60015473ffffffffffffffffffffffffffffffffffffffff16610320565b610246610508366004612e89565b611ca3565b60008061051c61010084612fc6565b9050600061052c61010085612fda565b73ffffffffffffffffffffffffffffffffffffffff8616600090815260046020908152604080832095835294905292909220546001921c82169091149150505b92915050565b60005b818110156105da57600083838381811061059157610591612fee565b9050602002013590506105a43382611d53565b604051339082907f8dd3c361eb2366ff27c2db0eb07b9261f1d052570742ab8c9a0c326f37aa576d90600090a350600101610575565b505050565b336000818152600660205260408082208490555183917f863123978d9b13946753a916c935c0688a01802440d3ffc668d04d2720c4e11091a350565b61062481611e29565b6000610637610100830160e08401612e89565b73ffffffffffffffffffffffffffffffffffffffff1614158015610681575033610668610100830160e08401612e89565b73ffffffffffffffffffffffffffffffffffffffff1614155b156106b8576040517fa7202ef600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610701336106cc6060840160408501612e89565b6101608401356101408501356106ea61012087016101008801612e89565b6106fc61014088016101208901612ded565b6120eb565b61073f6107146060830160408401612e89565b8460c084013560a085013561072f6080870160608801612e89565b6106fc60a0880160808901612ded565b60006107536101a083016101808401612e89565b73ffffffffffffffffffffffffffffffffffffffff16146107a2576107a2336107846101a084016101808501612e89565b6101a08401356101408501356106ea61012087016101008801612e89565b600754600090612710906107bb9061016085013561301d565b6107c59190612fc6565b905080156108175760085461081790339073ffffffffffffffffffffffffffffffffffffffff168361014086013561080561012088016101008901612e89565b6106fc61014089016101208a01612ded565b61082f61082a6080840160608501612e89565b6122bc565b15610965576000806108476080850160608601612e89565b6040517f2a55205a00000000000000000000000000000000000000000000000000000000815260a08601356004820152610160860135602482015273ffffffffffffffffffffffffffffffffffffffff9190911690632a55205a906044016040805180830381865afa1580156108c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e59190613034565b909250905080156109625784811115610932576040517f9c08743a000000000000000000000000000000000000000000000000000000008152600481018290526024015b60405180910390fd5b6109623383836101408801356109506101208a016101008b01612e89565b6106fc6101408b016101208c01612ded565b50505b336109766060840160408501612e89565b73ffffffffffffffffffffffffffffffffffffffff1683357f182e847dc18073123e8aa17e204b9e3874caf71387e52fe3083ffb98716d3d6b60c086013560a08701356109c96080890160608a01612e89565b6101608901356101408a01356109e76101208c016101008d01612e89565b6109f96101a08d016101808e01612e89565b60408051978852602088019690965273ffffffffffffffffffffffffffffffffffffffff9485169587019590955260608601929092526080850152811660a08401521660c08201526101a087013560e08201526101000160405180910390a450505050565b60408051601080825261022082019092526060916000919060208201610200803683370190505090506000467f00000000000000000000000000000000000000000000000000000000000003af14610afb577f436861696e49644368616e6765640000000000000000000000000000000000008282610adc81613062565b935081518110610aee57610aee612fee565b6020026020010181815250505b6000610b0d6060860160408701612e89565b73ffffffffffffffffffffffffffffffffffffffff8082166000908152600560205260409020549192501615610b665773ffffffffffffffffffffffffffffffffffffffff908116600090815260056020526040902054165b610bec81610b7387612394565b6101e088018035906102008a013590610b90906101c08c0161309a565b604051602001610bd893929190928352602083019190915260f81b7fff0000000000000000000000000000000000000000000000000000000000000016604082015260410190565b6040516020818303038152906040526126ad565b610c3f577f556e617574686f72697a656400000000000000000000000000000000000000008383610c1c81613062565b945081518110610c2e57610c2e612fee565b602002602001018181525050610cf4565b610c4a81863561050d565b15610c7b577f4e6f6e6365416c726561647955736564000000000000000000000000000000008383610c1c81613062565b73ffffffffffffffffffffffffffffffffffffffff811660009081526006602052604090205485351015610cf4577f4e6f6e6365546f6f4c6f770000000000000000000000000000000000000000008383610cd581613062565b945081518110610ce757610ce7612fee565b6020026020010181815250505b4285602001351015610d4b577f4f726465724578706972656400000000000000000000000000000000000000008383610d2c81613062565b945081518110610d3e57610d3e612fee565b6020026020010181815250505b6000610d5e610100870160e08801612e89565b73ffffffffffffffffffffffffffffffffffffffff1614158015610dbe575073ffffffffffffffffffffffffffffffffffffffff8616610da5610100870160e08801612e89565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610e0e577f53656e646572496e76616c6964000000000000000000000000000000000000008383610def81613062565b945081518110610e0157610e01612fee565b6020026020010181815250505b6000600981610e2561014089016101208a01612ded565b7fffffffff0000000000000000000000000000000000000000000000000000000016815260208101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff16905080610ec4577f53656e646572546f6b656e4b696e64556e6b6e6f776e000000000000000000008484610ea181613062565b955081518110610eb357610eb3612fee565b602002602001018181525050611536565b7fffffffff000000000000000000000000000000000000000000000000000000007f36372b070000000000000000000000000000000000000000000000000000000016610f1961014088016101208901612ded565b7fffffffff000000000000000000000000000000000000000000000000000000001614610f6c577f53656e646572546f6b656e496e76616c696400000000000000000000000000008484610ea181613062565b60075460009061271090610f85906101608a013561301d565b610f8f9190612fc6565b905060006101a0880135610fa8836101608b01356130bd565b610fb291906130bd565b9050610fc761082a60808a0160608b01612e89565b1561108e576000610fde60808a0160608b01612e89565b6040517f2a55205a00000000000000000000000000000000000000000000000000000000815260a08b013560048201526101608b0135602482015273ffffffffffffffffffffffffffffffffffffffff9190911690632a55205a906044016040805180830381865afa158015611058573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061107c9190613034565b915061108a905081836130bd565b9150505b6040805160a0810190915273ffffffffffffffffffffffffffffffffffffffff8a168152600090602081016110cb6101208c016101008d01612e89565b73ffffffffffffffffffffffffffffffffffffffff1681526020016110f86101408c016101208d01612ded565b7fffffffff000000000000000000000000000000000000000000000000000000001681526101408b01356020820152604001839052905073ffffffffffffffffffffffffffffffffffffffff8a16156113a757604080517f3170f63d000000000000000000000000000000000000000000000000000000008152825173ffffffffffffffffffffffffffffffffffffffff9081166004830152602084015181166024830152918301517fffffffff00000000000000000000000000000000000000000000000000000000166044820152606083015160648201526080830151608482015290851690633170f63d9060a401602060405180830381865afa158015611206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061122a91906130d0565b611279577f53656e646572416c6c6f77616e63654c6f770000000000000000000000000000878761125a81613062565b98508151811061126c5761126c612fee565b6020026020010181815250505b604080517fd1190df9000000000000000000000000000000000000000000000000000000008152825173ffffffffffffffffffffffffffffffffffffffff9081166004830152602084015181166024830152918301517fffffffff0000000000000000000000000000000000000000000000000000000016604482015260608301516064820152608083015160848201529085169063d1190df99060a401602060405180830381865afa158015611334573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061135891906130d0565b6113a7577f53656e64657242616c616e63654c6f7700000000000000000000000000000000878761138881613062565b98508151811061139a5761139a612fee565b6020026020010181815250505b604080517f40944de9000000000000000000000000000000000000000000000000000000008152825173ffffffffffffffffffffffffffffffffffffffff9081166004830152602084015181166024830152918301517fffffffff000000000000000000000000000000000000000000000000000000001660448201526060830151606482015260808301516084820152908516906340944de99060a401602060405180830381865afa158015611462573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061148691906130d0565b6114d5577f416d6f756e744f724944496e76616c696400000000000000000000000000000087876114b681613062565b9850815181106114c8576114c8612fee565b6020026020010181815250505b6101a08901356101608a01351015611532577f416666696c69617465416d6f756e74496e76616c696400000000000000000000878761151381613062565b98508151811061152557611525612fee565b6020026020010181815250505b5050505b600060098161154b60a08a0160808b01612ded565b7fffffffff0000000000000000000000000000000000000000000000000000000016815260208101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff169050806115ea577f5369676e6572546f6b656e4b696e64556e6b6e6f776e0000000000000000000085856115c781613062565b9650815181106115d9576115d9612fee565b602002602001018181525050611896565b604080517f3170f63d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831691633170f63d9161163e918b0190600401613174565b602060405180830381865afa15801561165b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167f91906130d0565b6116ce577f5369676e6572416c6c6f77616e63654c6f77000000000000000000000000000085856116af81613062565b9650815181106116c1576116c1612fee565b6020026020010181815250505b604080517fd1190df900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83169163d1190df991611722918b0190600401613174565b602060405180830381865afa15801561173f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176391906130d0565b6117b2577f5369676e657242616c616e63654c6f7700000000000000000000000000000000858561179381613062565b9650815181106117a5576117a5612fee565b6020026020010181815250505b604080517f40944de900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316916340944de991611806918b0190600401613174565b602060405180830381865afa158015611823573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061184791906130d0565b611896577f416d6f756e744f724944496e76616c6964000000000000000000000000000000858561187781613062565b96508151811061188957611889612fee565b6020026020010181815250505b845184146118a2578385525b50929695505050505050565b6118b661272a565b6118c060006127ab565b565b6118ca61272a565b6127108110611905576040517f52dadcf900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60078190556040518181527fdc0410a296e1e33943a772020d333d5f99319d7fcad932a484c53889f7aaa2b19060200160405180910390a150565b600154339073ffffffffffffffffffffffffffffffffffffffff1681146119e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f74207468652060448201527f6e6577206f776e657200000000000000000000000000000000000000000000006064820152608401610929565b6119f2816127ab565b50565b6119fd61272a565b73ffffffffffffffffffffffffffffffffffffffff8116611a4a576040517f6b8df36300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f8b2a800ce9e2e7ccdf4741ae0e41b1f16983192291080ae3b78ac4296ddf598a90600090a250565b600060608082808083611aed7f535741500000000000000000000000000000000000000000000000000000000460026127dc565b611b187f342e32000000000000000000000000000000000000000000000000000000000360036127dc565b604080516000808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009b939a50919850469750309650945092509050565b3360008181526005602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000008116909155905173ffffffffffffffffffffffffffffffffffffffff909116929183917fd7426110292f20fe59e73ccf52124e0f5440a756507c91c7b0a6c50e1eb1a23a9190a350565b73ffffffffffffffffffffffffffffffffffffffff8116611c28576040517fcd4b78cf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360008181526005602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8616908117909155905190917f30468de898bda644e26bab66e5a2241a3aa6aaf527257f5ca54e0f65204ba14a91a350565b611cab61272a565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff00000000000000000000000000000000000000009091168117909155611d0e60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6000611d6161010083612fc6565b90506000611d7161010084612fda565b73ffffffffffffffffffffffffffffffffffffffff85166000908152600460209081526040808320868452909152902054909150600181831c81169003611de7576040517f91cab50400000000000000000000000000000000000000000000000000000000815260048101859052602401610929565b73ffffffffffffffffffffffffffffffffffffffff909416600090815260046020908152604080832094835293905291909120600190911b9290921790915550565b467f00000000000000000000000000000000000000000000000000000000000003af14611e82576040517fc614eff800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffff000000000000000000000000000000000000000000000000000000007f36372b070000000000000000000000000000000000000000000000000000000016611ed761014083016101208401612ded565b7fffffffff000000000000000000000000000000000000000000000000000000001614611f30576040517fd0bd721a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101a08101356101608201351015611f74576040517f3b71bbca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611f866060830160408401612e89565b73ffffffffffffffffffffffffffffffffffffffff8082166000908152600560205260409020549192501615611fdf5773ffffffffffffffffffffffffffffffffffffffff908116600090815260056020526040902054165b61200981611fec84612394565b6101e0850180359061020087013590610b90906101c0890161309a565b61203f576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61204a818335611d53565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260066020526040902054823510156120aa576040517fd24d82a400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b428260200135116120e7576040517fc56873ba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b7fffffffff00000000000000000000000000000000000000000000000000000000811660009081526009602052604090205473ffffffffffffffffffffffffffffffffffffffff168061216a576040517f33c8207c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805173ffffffffffffffffffffffffffffffffffffffff89811660248301528881166044830152606482018890526084820187905285811660a4808401919091528351808403909101815260c490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f47338bc300000000000000000000000000000000000000000000000000000000179052915160009284169161221791613182565b600060405180830381855af49150503d8060008114612252576040519150601f19603f3d011682016040523d82523d6000602084013e612257565b606091505b50509050806122b2576040517f523c3aaa00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808a16600483015288166024820152604401610929565b5050505050505050565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f2a55205a00000000000000000000000000000000000000000000000000000000600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906301ffc9a790602401602060405180830381865afa925050508015612383575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252612380918101906130d0565b60015b61056c57506000919050565b919050565b60007f8c466c1040f056c2de0a3a0961d6eccf8c51d50d91f69d55f4cdc6ca3c528f6d6040516020016124f4907f4f726465722875696e74323536206e6f6e63652c75696e74323536206578706981527f72792c75696e743235362070726f746f636f6c4665652c50617274792073696760208201527f6e65722c50617274792073656e6465722c6164647265737320616666696c696160408201527f746557616c6c65742c75696e7432353620616666696c69617465416d6f756e7460608201527f290000000000000000000000000000000000000000000000000000000000000060808201527f506172747928616464726573732077616c6c65742c6164647265737320746f6b60818201527f656e2c627974657334206b696e642c75696e743235362069642c75696e74323560a18201527f3620616d6f756e7429000000000000000000000000000000000000000000000060c182015260ca0190565b60405160208183030381529060405280519060200120836000013584602001356007547f224ed05dccb4f5c1e21e6e85fb29dd6c83074e33458fdfefb184d03fe8742f4f8760400160405160200161254d92919061319e565b604051602081830303815290604052805190602001207f224ed05dccb4f5c1e21e6e85fb29dd6c83074e33458fdfefb184d03fe8742f4f8860e00160405160200161259992919061319e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101206125e16101a08a016101808b01612e89565b6040805160208101989098528701959095526060860193909352608085019190915260a084015260c083015273ffffffffffffffffffffffffffffffffffffffff1660e08201526101a084013561010082015261012001604051602081830303815290604052805190602001206040516020016126909291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604051602081830303815290604052805190602001209050919050565b60008060006126bc8585612887565b909250905060008160048111156126d5576126d56131b2565b14801561270d57508573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b8061271e575061271e8686866128cc565b925050505b9392505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146118c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610929565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556119f281612a29565b606060ff83146127f6576127ef83612a9e565b905061056c565b818054612802906131e1565b80601f016020809104026020016040519081016040528092919081815260200182805461282e906131e1565b801561287b5780601f106128505761010080835404028352916020019161287b565b820191906000526020600020905b81548152906001019060200180831161285e57829003601f168201915b5050505050905061056c565b60008082516041036128bd5760208301516040840151606085015160001a6128b187828585612add565b945094505050506128c5565b506000905060025b9250929050565b60008060008573ffffffffffffffffffffffffffffffffffffffff16631626ba7e60e01b868660405160240161290392919061322e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161298c9190613182565b600060405180830381855afa9150503d80600081146129c7576040519150601f19603f3d011682016040523d82523d6000602084013e6129cc565b606091505b50915091508180156129e057506020815110155b801561271e575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090612a1e908301602090810190840161324f565b149695505050505050565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60606000612aab83612bcc565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115612b145750600090506003612bc3565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015612b68573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff8116612bbc57600060019250925050612bc3565b9150600090505b94509492505050565b600060ff8216601f81111561056c576040517fb3512b0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811681146119f257600080fd5b60008060408385031215612c4257600080fd5b8235612c4d81612c0d565b946020939093013593505050565b60008060208385031215612c6e57600080fd5b823567ffffffffffffffff80821115612c8657600080fd5b818501915085601f830112612c9a57600080fd5b813581811115612ca957600080fd5b8660208260051b8501011115612cbe57600080fd5b60209290920196919550909350505050565b600060208284031215612ce257600080fd5b5035919050565b60006102208284031215612cfc57600080fd5b50919050565b60008060006102608486031215612d1857600080fd5b8335612d2381612c0d565b925060208401359150612d398560408601612ce9565b90509250925092565b6000806102408385031215612d5657600080fd5b8235612d6181612c0d565b9150612d708460208501612ce9565b90509250929050565b6020808252825182820181905260009190848201906040850190845b81811015612db157835183529284019291840191600101612d95565b50909695505050505050565b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461238f57600080fd5b600060208284031215612dff57600080fd5b61272382612dbd565b60005b83811015612e23578181015183820152602001612e0b565b50506000910152565b60008151808452612e44816020860160208601612e08565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006127236020830184612e2c565b600060208284031215612e9b57600080fd5b813561272381612c0d565b7fff00000000000000000000000000000000000000000000000000000000000000881681526000602060e06020840152612ee360e084018a612e2c565b8381036040850152612ef5818a612e2c565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c08601528551808252602080880193509091019060005b81811015612f5657835183529284019291840191600101612f3a565b50909c9b505050505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082612fd557612fd5612f68565b500490565b600082612fe957612fe9612f68565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b808202811582820484141761056c5761056c612f97565b6000806040838503121561304757600080fd5b825161305281612c0d565b6020939093015192949293505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361309357613093612f97565b5060010190565b6000602082840312156130ac57600080fd5b813560ff8116811461272357600080fd5b8082018082111561056c5761056c612f97565b6000602082840312156130e257600080fd5b8151801515811461272357600080fd5b80356130fd81612c0d565b73ffffffffffffffffffffffffffffffffffffffff908116835260208201359061312682612c0d565b1660208301527fffffffff0000000000000000000000000000000000000000000000000000000061315960408301612dbd565b16604083015260608181013590830152608090810135910152565b60a0810161056c82846130f2565b60008251613194818460208701612e08565b9190910192915050565b82815260c0810161272360208301846130f2565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600181811c908216806131f557607f821691505b602082108103612cfc577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b8281526040602082015260006132476040830184612e2c565b949350505050565b60006020828403121561326157600080fd5b505191905056fea2646970667358221220b1ca1e33580bf8fa9028f5451f5caaaa05116d8c26193b7169108ad944e789e364736f6c63430008170033