Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- BatchCall
- Optimization enabled
- true
- Compiler version
- v0.8.23+commit.f704f362
- Optimization runs
- 999999
- EVM Version
- paris
- Verified at
- 2024-04-16T09:52:13.563151Z
contracts/BatchCall.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import { ERC20 } from "solady/src/tokens/ERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@airswap/swap/contracts/interfaces/ISwap.sol";
import "@airswap/swap-erc20/contracts/interfaces/ISwapERC20.sol";
import "@airswap/registry/contracts/interfaces/IRegistry.sol";
/**
* @title BatchCall: Batch balance, allowance, order validity checks, nonce usage check
*/
contract BatchCall {
using Address for address;
error ArgumentInvalid();
/**
* @notice Check the token balance of a wallet in a token contract
* @dev return 0 on returns 0 on invalid spender contract or non-contract address
* @param userAddress address
* @param tokenAddress address
* @return uint256 token balance if possible
*/
function tokenBalance(
address userAddress,
address tokenAddress
) public view returns (uint256) {
if (tokenAddress.isContract()) {
ERC20 token = ERC20(tokenAddress);
// Check if balanceOf succeeds.
(bool success, ) = address(token).staticcall(
abi.encodeWithSelector(token.balanceOf.selector, userAddress)
);
if (success) {
return token.balanceOf(userAddress);
}
return 0;
}
return 0;
}
/**
* @notice Check the token balances of a wallet for multiple tokens
* @dev return array and will fail if large token arrays are inputted
* @dev Returns array of token balances in base units
* @param userAddress address
* @param tokenAddresses address[]
* @return uint256[] token balance array if possible
*/
function walletBalances(
address userAddress,
address[] calldata tokenAddresses
) external view returns (uint256[] memory) {
if (tokenAddresses.length <= 0) revert ArgumentInvalid();
uint256[] memory balances = new uint256[](tokenAddresses.length);
for (uint256 i; i < tokenAddresses.length; ) {
if (tokenAddresses[i] != address(0)) {
balances[i] = tokenBalance(userAddress, tokenAddresses[i]);
} else {
balances[i] = userAddress.balance;
}
unchecked {
++i;
}
}
return balances;
}
/**
* @notice Check the token balances of multiple wallets for multiple tokens
* @dev return array and will fail if large arrays are inputted
* @dev Returns array of token balances in base units
* @param userAddresses address[]
* @param tokenAddresses address[]
* @return uint256[] token allowances array if possible
*/
function allBalancesForManyAccounts(
address[] calldata userAddresses,
address[] calldata tokenAddresses
) external view returns (uint256[] memory) {
uint256[] memory balances = new uint256[](
tokenAddresses.length * userAddresses.length
);
for (uint256 i; i < userAddresses.length; ) {
for (uint256 j; j < tokenAddresses.length; ) {
if (tokenAddresses[j] != address(0)) {
balances[(i * tokenAddresses.length) + j] = tokenBalance(
userAddresses[i],
tokenAddresses[j]
);
} else {
balances[(i * tokenAddresses.length) + j] = userAddresses[i].balance;
}
unchecked {
++j;
}
}
unchecked {
++i;
}
}
return balances;
}
/**
* @notice Check the token allowance of a wallet in a token contract
* @dev return 0 on returns 0 on invalid spender contract or non-contract address
* @param userAddress address
* @param spenderAddress address Specified address to spend
* @param tokenAddress address
* @return uint256 token allowance if possible
*/
function tokenAllowance(
address userAddress,
address spenderAddress,
address tokenAddress
) public view returns (uint256) {
if (tokenAddress.isContract()) {
ERC20 token = ERC20(tokenAddress);
// Check if allowance succeeds as a call else returns 0.
(bool success, ) = address(token).staticcall(
abi.encodeWithSelector(
token.allowance.selector,
userAddress,
spenderAddress
)
);
if (success) {
return token.allowance(userAddress, spenderAddress);
}
return 0;
}
return 0;
}
/**
* @notice Check the token allowances of a wallet for multiple tokens
* @dev return array and will fail if large token arrays are inputted
* @dev Returns array of token allowances in base units
* @param userAddress address
* @param spenderAddress address
* @param tokenAddresses address[]
* @return uint256[] token allowances array if possible
*/
function walletAllowances(
address userAddress,
address spenderAddress,
address[] calldata tokenAddresses
) external view returns (uint256[] memory) {
if (tokenAddresses.length <= 0) revert ArgumentInvalid();
uint256[] memory allowances = new uint256[](tokenAddresses.length);
for (uint256 i; i < tokenAddresses.length; ) {
allowances[i] = tokenAllowance(
userAddress,
spenderAddress,
tokenAddresses[i]
);
unchecked {
++i;
}
}
return allowances;
}
/**
* @notice Check the token allowances of multiple wallets for multiple tokens
* @dev return array and will fail if large arrays are inputted
* @dev Returns array of token allowances in base units
* @param userAddresses address[]
* @param spenderAddress address
* @param tokenAddresses address[]
* @return uint256[] token allowances array if possible
*/
function allAllowancesForManyAccounts(
address[] calldata userAddresses,
address spenderAddress,
address[] calldata tokenAddresses
) external view returns (uint256[] memory) {
uint256[] memory allowances = new uint256[](
tokenAddresses.length * userAddresses.length
);
for (uint256 i; i < userAddresses.length; ) {
for (uint256 j; j < tokenAddresses.length; ) {
allowances[(i * tokenAddresses.length) + j] = tokenAllowance(
userAddresses[i],
spenderAddress,
tokenAddresses[j]
);
unchecked {
++j;
}
}
unchecked {
++i;
}
}
return allowances;
}
/**
* @notice Check validity of an array of Orders
* @param senderWallet address Wallet that would send the order
* @param orders ISwap.Order[] Array of orders to be checked
* @param swapContract ISwap Swap contract to call
* @return bool[] True indicates the order is valid
*/
function getOrdersValid(
address senderWallet,
ISwap.Order[] calldata orders,
ISwap swapContract
) external view returns (bool[] memory) {
if (orders.length <= 0) revert ArgumentInvalid();
bool[] memory orderValidity = new bool[](orders.length);
for (uint256 i; i < orders.length; ) {
bytes32[] memory errors = swapContract.check(senderWallet, orders[i]);
orderValidity[i] = errors.length == 0 ? true : false;
unchecked {
++i;
}
}
return orderValidity;
}
/**
* @notice Check validity of an array of OrderERC20s
* @param senderWallet address Wallet that would send the order
* @param orders ISwapERC20.OrderERC20[] Array of orders to be checked
* @param swapERC20Contract ISwapERC20 Swap contract to call
* @return bool[] True indicates the order is valid
*/
function getOrdersValidERC20(
address senderWallet,
ISwapERC20.OrderERC20[] calldata orders,
ISwapERC20 swapERC20Contract
) external view returns (bool[] memory) {
if (orders.length <= 0) revert ArgumentInvalid();
bool[] memory orderValidity = new bool[](orders.length);
for (uint256 i; i < orders.length; ) {
ISwapERC20.OrderERC20 memory order = orders[i];
bytes32[] memory errors = swapERC20Contract.check(
senderWallet,
order.nonce,
order.expiry,
order.signerWallet,
order.signerToken,
order.signerAmount,
order.senderToken,
order.senderAmount,
order.v,
order.r,
order.s
);
orderValidity[i] = errors.length == 0 ? true : false;
unchecked {
++i;
}
}
return orderValidity;
}
/**
* @notice Checks usage of an array of nonces
* @dev Swap and SwapERC20 nonceUsed function have the same signature
* @param signerWallets address[] list of signers for each nonce
* @param nonces uint256[] list of nonces to be checked
* @param swapContract ISwap[] Swap or SwapERC20 contract to call
* @return bool[] true indicates the nonce is used
*/
function getNoncesUsed(
address[] calldata signerWallets,
uint256[] calldata nonces,
ISwap swapContract
) external view returns (bool[] memory) {
if (signerWallets.length == 0) revert ArgumentInvalid();
if (signerWallets.length != nonces.length) revert ArgumentInvalid();
bool[] memory nonceUsed = new bool[](signerWallets.length);
for (uint256 i; i < signerWallets.length; ) {
nonceUsed[i] = swapContract.nonceUsed(signerWallets[i], nonces[i]);
unchecked {
++i;
}
}
return nonceUsed;
}
/**
* @notice provides the tokens supported by multiple Stakers
* @param stakers address[] list of stakers to be checked
* @param registryContract IRegistry Registry contract to call
* @return bool[] true indicates the nonce is used
*/
function getTokensForStakers(
address[] calldata stakers,
IRegistry registryContract
) external view returns (address[][] memory) {
if (stakers.length == 0) revert ArgumentInvalid();
address[][] memory tokensSupported = new address[][](stakers.length);
for (uint256 i; i < stakers.length; ) {
tokensSupported[i] = registryContract.getTokensForStaker(stakers[i]);
unchecked {
++i;
}
}
return tokensSupported;
}
}
@airswap/registry/contracts/interfaces/IRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
interface IRegistry {
event SetServerURL(address indexed staker, string url);
event AddProtocols(address indexed staker, bytes4[] protocols);
event AddTokens(address indexed staker, address[] tokens);
event RemoveProtocols(address indexed staker, bytes4[] protocols);
event RemoveTokens(address indexed staker, address[] tokens);
event UnsetServer(
address indexed staker,
string url,
bytes4[] protocols,
address[] tokens
);
error ArgumentInvalid();
error NoServerURLSet();
error ProtocolDoesNotExist(bytes4);
error ProtocolExists(bytes4);
error TokenDoesNotExist(address);
error TokenExists(address);
error ServerURLInvalid();
function setServerURL(string calldata _url) external;
function unsetServer() external;
function addProtocols(bytes4[] calldata _protocols) external;
function removeProtocols(bytes4[] calldata _protocols) external;
function getServerURLsForProtocol(
bytes4 _protocol
) external view returns (string[] memory _urls);
function supportsProtocol(
address _staker,
bytes4 _protocol
) external view returns (bool);
function getProtocolsForStaker(
address _staker
) external view returns (bytes4[] memory _protocolList);
function getStakersForProtocol(
bytes4 _protocol
) external view returns (address[] memory _stakers);
function addTokens(address[] calldata _tokens) external;
function removeTokens(address[] calldata _tokens) external;
function getServerURLsForToken(
address _token
) external view returns (string[] memory urls);
function supportsToken(
address _staker,
address _token
) external view returns (bool);
function getTokensForStaker(
address _staker
) external view returns (address[] memory tokenList);
function getStakersForToken(
address _token
) external view returns (address[] memory _stakers);
function getServerURLsForStakers(
address[] calldata _stakers
) external view returns (string[] memory _urls);
function balanceOf(address _staker) external view returns (uint256);
}
@airswap/swap-erc20/contracts/interfaces/ISwapERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
interface ISwapERC20 {
struct OrderERC20 {
uint256 nonce; // Unique number per signatory per order
uint256 expiry; // Expiry time (seconds since unix epoch)
address signerWallet; // Party to the swap that sets terms
address signerToken; // ERC20 token address transferred from signer
uint256 signerAmount; // Amount of tokens transferred from signer
address senderWallet; // Party to the swap that accepts terms
address senderToken; // ERC20 token address transferred from sender
uint256 senderAmount; // Amount of tokens transferred from sender
uint8 v; // ECDSA
bytes32 r;
bytes32 s;
}
event SwapERC20(uint256 indexed nonce, address indexed signerWallet);
event Cancel(uint256 indexed nonce, address indexed signerWallet);
event Authorize(address indexed signer, address indexed signerWallet);
event Revoke(address indexed signer, address indexed signerWallet);
event SetProtocolFee(uint256 protocolFee);
event SetProtocolFeeLight(uint256 protocolFeeLight);
event SetProtocolFeeWallet(address indexed feeWallet);
event SetBonusScale(uint256 bonusScale);
event SetBonusMax(uint256 bonusMax);
event SetStaking(address indexed staking);
error ChainIdChanged();
error InvalidFee();
error InvalidFeeLight();
error InvalidFeeWallet();
error InvalidStaking();
error OrderExpired();
error MaxTooHigh();
error NonceAlreadyUsed(uint256);
error ScaleTooHigh();
error SignatoryInvalid();
error SignatureInvalid();
error TransferFromFailed();
function swap(
address recipient,
uint256 nonce,
uint256 expiry,
address signerWallet,
address signerToken,
uint256 signerAmount,
address senderToken,
uint256 senderAmount,
uint8 v,
bytes32 r,
bytes32 s
) external;
function swapAnySender(
address recipient,
uint256 nonce,
uint256 expiry,
address signerWallet,
address signerToken,
uint256 signerAmount,
address senderToken,
uint256 senderAmount,
uint8 v,
bytes32 r,
bytes32 s
) external;
function swapLight(
uint256 nonce,
uint256 expiry,
address signerWallet,
address signerToken,
uint256 signerAmount,
address senderToken,
uint256 senderAmount,
uint8 v,
bytes32 r,
bytes32 s
) external;
function authorize(address sender) external;
function revoke() external;
function cancel(uint256[] calldata nonces) external;
function check(
address senderWallet,
uint256 nonce,
uint256 expiry,
address signerWallet,
address signerToken,
uint256 signerAmount,
address senderToken,
uint256 senderAmount,
uint8 v,
bytes32 r,
bytes32 s
) external view returns (bytes32[] memory);
function nonceUsed(address, uint256) external view returns (bool);
function authorized(address) external view returns (address);
function calculateProtocolFee(
address,
uint256
) external view returns (uint256);
}
@airswap/swap/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;
}
@airswap/swap/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);
}
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
solady/src/tokens/ERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple ERC20 + EIP-2612 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
///
/// @dev Note:
/// - The ERC20 standard allows minting and transferring to and from the zero address,
/// minting and transferring zero tokens, as well as self-approvals.
/// For performance, this implementation WILL NOT revert for such actions.
/// Please add any checks with overrides if desired.
/// - The `permit` function uses the ecrecover precompile (0x1).
///
/// If you are overriding:
/// - NEVER violate the ERC20 invariant:
/// the total sum of all balances must be equal to `totalSupply()`.
/// - Check that the overridden function is actually used in the function you want to
/// change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC20 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The total supply has overflowed.
error TotalSupplyOverflow();
/// @dev The allowance has overflowed.
error AllowanceOverflow();
/// @dev The allowance has underflowed.
error AllowanceUnderflow();
/// @dev Insufficient balance.
error InsufficientBalance();
/// @dev Insufficient allowance.
error InsufficientAllowance();
/// @dev The permit is invalid.
error InvalidPermit();
/// @dev The permit has expired.
error PermitExpired();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
event Transfer(address indexed from, address indexed to, uint256 amount);
/// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
event Approval(address indexed owner, address indexed spender, uint256 amount);
/// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
uint256 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
/// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
uint256 private constant _APPROVAL_EVENT_SIGNATURE =
0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The storage slot for the total supply.
uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;
/// @dev The balance slot of `owner` is given by:
/// ```
/// mstore(0x0c, _BALANCE_SLOT_SEED)
/// mstore(0x00, owner)
/// let balanceSlot := keccak256(0x0c, 0x20)
/// ```
uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;
/// @dev The allowance slot of (`owner`, `spender`) is given by:
/// ```
/// mstore(0x20, spender)
/// mstore(0x0c, _ALLOWANCE_SLOT_SEED)
/// mstore(0x00, owner)
/// let allowanceSlot := keccak256(0x0c, 0x34)
/// ```
uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;
/// @dev The nonce slot of `owner` is given by:
/// ```
/// mstore(0x0c, _NONCES_SLOT_SEED)
/// mstore(0x00, owner)
/// let nonceSlot := keccak256(0x0c, 0x20)
/// ```
uint256 private constant _NONCES_SLOT_SEED = 0x38377508;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 private constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev `keccak256("1")`.
bytes32 private constant _VERSION_HASH =
0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
/// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
bytes32 private constant _PERMIT_TYPEHASH =
0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 METADATA */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the name of the token.
function name() public view virtual returns (string memory);
/// @dev Returns the symbol of the token.
function symbol() public view virtual returns (string memory);
/// @dev Returns the decimals places of the token.
function decimals() public view virtual returns (uint8) {
return 18;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the amount of tokens in existence.
function totalSupply() public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_TOTAL_SUPPLY_SLOT)
}
}
/// @dev Returns the amount of tokens owned by `owner`.
function balanceOf(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
function allowance(address owner, address spender)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x34))
}
}
/// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
///
/// Emits a {Approval} event.
function approve(address spender, uint256 amount) public virtual returns (bool) {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and store the amount.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x34), amount)
// Emit the {Approval} event.
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
}
return true;
}
/// @dev Transfer `amount` tokens from the caller to `to`.
///
/// Requirements:
/// - `from` must at least have `amount`.
///
/// Emits a {Transfer} event.
function transfer(address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(msg.sender, to, amount);
/// @solidity memory-safe-assembly
assembly {
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, caller())
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
}
_afterTokenTransfer(msg.sender, to, amount);
return true;
}
/// @dev Transfers `amount` tokens from `from` to `to`.
///
/// Note: Does not update the allowance if it is the maximum uint256 value.
///
/// Requirements:
/// - `from` must at least have `amount`.
/// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
///
/// Emits a {Transfer} event.
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(from, to, amount);
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
// Compute the allowance slot and load its value.
mstore(0x20, caller())
mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
// If the allowance is not the maximum uint256 value.
if add(allowance_, 1) {
// Revert if the amount to be transferred exceeds the allowance.
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
sstore(allowanceSlot, sub(allowance_, amount))
}
// Compute the balance slot and load its value.
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
return true;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-2612 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev For more performance, override to return the constant value
/// of `keccak256(bytes(name()))` if `name()` will never change.
function _constantNameHash() internal view virtual returns (bytes32 result) {}
/// @dev Returns the current nonce for `owner`.
/// This value is used to compute the signature for EIP-2612 permit.
function nonces(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
// Compute the nonce slot and load its value.
mstore(0x0c, _NONCES_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
/// authorized by a signed approval by `owner`.
///
/// Emits a {Approval} event.
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
bytes32 nameHash = _constantNameHash();
// We simply calculate it on-the-fly to allow for cases where the `name` may change.
if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
/// @solidity memory-safe-assembly
assembly {
// Revert if the block timestamp is greater than `deadline`.
if gt(timestamp(), deadline) {
mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
revert(0x1c, 0x04)
}
let m := mload(0x40) // Grab the free memory pointer.
// Clean the upper 96 bits.
owner := shr(96, shl(96, owner))
spender := shr(96, shl(96, spender))
// Compute the nonce slot and load its value.
mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
mstore(0x00, owner)
let nonceSlot := keccak256(0x0c, 0x20)
let nonceValue := sload(nonceSlot)
// Prepare the domain separator.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), _VERSION_HASH)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
mstore(0x2e, keccak256(m, 0xa0))
// Prepare the struct hash.
mstore(m, _PERMIT_TYPEHASH)
mstore(add(m, 0x20), owner)
mstore(add(m, 0x40), spender)
mstore(add(m, 0x60), value)
mstore(add(m, 0x80), nonceValue)
mstore(add(m, 0xa0), deadline)
mstore(0x4e, keccak256(m, 0xc0))
// Prepare the ecrecover calldata.
mstore(0x00, keccak256(0x2c, 0x42))
mstore(0x20, and(0xff, v))
mstore(0x40, r)
mstore(0x60, s)
let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20)
// If the ecrecover fails, the returndatasize will be 0x00,
// `owner` will be checked if it equals the hash at 0x00,
// which evaluates to false (i.e. 0), and we will revert.
// If the ecrecover succeeds, the returndatasize will be 0x20,
// `owner` will be compared against the returned address at 0x20.
if iszero(eq(mload(returndatasize()), owner)) {
mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
revert(0x1c, 0x04)
}
// Increment and store the updated nonce.
sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
// Compute the allowance slot and store the value.
// The `owner` is already at slot 0x20.
mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
sstore(keccak256(0x2c, 0x34), value)
// Emit the {Approval} event.
log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
bytes32 nameHash = _constantNameHash();
// We simply calculate it on-the-fly to allow for cases where the `name` may change.
if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Grab the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), _VERSION_HASH)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
result := keccak256(m, 0xa0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL MINT FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Mints `amount` tokens to `to`, increasing the total supply.
///
/// Emits a {Transfer} event.
function _mint(address to, uint256 amount) internal virtual {
_beforeTokenTransfer(address(0), to, amount);
/// @solidity memory-safe-assembly
assembly {
let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
let totalSupplyAfter := add(totalSupplyBefore, amount)
// Revert if the total supply overflows.
if lt(totalSupplyAfter, totalSupplyBefore) {
mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
revert(0x1c, 0x04)
}
// Store the updated total supply.
sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
}
_afterTokenTransfer(address(0), to, amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL BURN FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Burns `amount` tokens from `from`, reducing the total supply.
///
/// Emits a {Transfer} event.
function _burn(address from, uint256 amount) internal virtual {
_beforeTokenTransfer(from, address(0), amount);
/// @solidity memory-safe-assembly
assembly {
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, from)
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Subtract and store the updated total supply.
sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
// Emit the {Transfer} event.
mstore(0x00, amount)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
}
_afterTokenTransfer(from, address(0), amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL TRANSFER FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Moves `amount` of tokens from `from` to `to`.
function _transfer(address from, address to, uint256 amount) internal virtual {
_beforeTokenTransfer(from, to, amount);
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
// Compute the balance slot and load its value.
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL ALLOWANCE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and load its value.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
// If the allowance is not the maximum uint256 value.
if add(allowance_, 1) {
// Revert if the amount to be transferred exceeds the allowance.
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
sstore(allowanceSlot, sub(allowance_, amount))
}
}
}
/// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
///
/// Emits a {Approval} event.
function _approve(address owner, address spender, uint256 amount) internal virtual {
/// @solidity memory-safe-assembly
assembly {
let owner_ := shl(96, owner)
// Compute the allowance slot and store the amount.
mstore(0x20, spender)
mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
sstore(keccak256(0x0c, 0x34), amount)
// Emit the {Approval} event.
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HOOKS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Hook that is called before any transfer of tokens.
/// This includes minting and burning.
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/// @dev Hook that is called after any transfer of tokens.
/// This includes minting and burning.
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
Compiler Settings
{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":999999,"enabled":true},"libraries":{},"evmVersion":"paris"}
Contract ABI
[{"type":"error","name":"ArgumentInvalid","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"allAllowancesForManyAccounts","inputs":[{"type":"address[]","name":"userAddresses","internalType":"address[]"},{"type":"address","name":"spenderAddress","internalType":"address"},{"type":"address[]","name":"tokenAddresses","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"allBalancesForManyAccounts","inputs":[{"type":"address[]","name":"userAddresses","internalType":"address[]"},{"type":"address[]","name":"tokenAddresses","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool[]","name":"","internalType":"bool[]"}],"name":"getNoncesUsed","inputs":[{"type":"address[]","name":"signerWallets","internalType":"address[]"},{"type":"uint256[]","name":"nonces","internalType":"uint256[]"},{"type":"address","name":"swapContract","internalType":"contract ISwap"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool[]","name":"","internalType":"bool[]"}],"name":"getOrdersValid","inputs":[{"type":"address","name":"senderWallet","internalType":"address"},{"type":"tuple[]","name":"orders","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":"address","name":"swapContract","internalType":"contract ISwap"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool[]","name":"","internalType":"bool[]"}],"name":"getOrdersValidERC20","inputs":[{"type":"address","name":"senderWallet","internalType":"address"},{"type":"tuple[]","name":"orders","internalType":"struct ISwapERC20.OrderERC20[]","components":[{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"uint256","name":"expiry","internalType":"uint256"},{"type":"address","name":"signerWallet","internalType":"address"},{"type":"address","name":"signerToken","internalType":"address"},{"type":"uint256","name":"signerAmount","internalType":"uint256"},{"type":"address","name":"senderWallet","internalType":"address"},{"type":"address","name":"senderToken","internalType":"address"},{"type":"uint256","name":"senderAmount","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"address","name":"swapERC20Contract","internalType":"contract ISwapERC20"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[][]","name":"","internalType":"address[][]"}],"name":"getTokensForStakers","inputs":[{"type":"address[]","name":"stakers","internalType":"address[]"},{"type":"address","name":"registryContract","internalType":"contract IRegistry"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tokenAllowance","inputs":[{"type":"address","name":"userAddress","internalType":"address"},{"type":"address","name":"spenderAddress","internalType":"address"},{"type":"address","name":"tokenAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tokenBalance","inputs":[{"type":"address","name":"userAddress","internalType":"address"},{"type":"address","name":"tokenAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"walletAllowances","inputs":[{"type":"address","name":"userAddress","internalType":"address"},{"type":"address","name":"spenderAddress","internalType":"address"},{"type":"address[]","name":"tokenAddresses","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"walletBalances","inputs":[{"type":"address","name":"userAddress","internalType":"address"},{"type":"address[]","name":"tokenAddresses","internalType":"address[]"}]}]
Contract Creation Code
0x608060405234801561001057600080fd5b50611c59806100206000396000f3fe608060405234801561001057600080fd5b50600436106100be5760003560e01c80634c6305801161007657806377a7d9681161005b57806377a7d968146101865780638e8758d814610199578063df8344fe146101ac57600080fd5b80634c6305801461016057806359c94ce41461017357600080fd5b806317c958eb116100a757806317c958eb1461010d5780631e61290d1461012d578063366dff9c1461014d57600080fd5b806301afd5f3146100c35780631049334f146100ec575b600080fd5b6100d66100d13660046111ac565b6101bf565b6040516100e39190611218565b60405180910390f35b6100ff6100fa366004611291565b61037f565b6040519081526020016100e3565b61012061011b3660046112ca565b610529565b6040516100e39190611366565b61014061013b3660046113a0565b6106ac565b6040516100e391906113f7565b6100d661015b3660046114b5565b610867565b61012061016e36600461150e565b610943565b610120610181366004611585565b610b8b565b6100d6610194366004611609565b610d78565b6100ff6101a736600461165e565b610ebf565b6100d66101ba36600461169e565b611079565b606060006101cd8584611752565b67ffffffffffffffff8111156101e5576101e5611769565b60405190808252806020026020018201604052801561020e578160200160208202803683370190505b50905060005b858110156103755760005b8481101561036c57600086868381811061023b5761023b611798565b905060200201602081019061025091906117c7565b73ffffffffffffffffffffffffffffffffffffffff16146102f3576102bc88888481811061028057610280611798565b905060200201602081019061029591906117c7565b8787848181106102a7576102a7611798565b90506020020160208101906100fa91906117c7565b83826102c88886611752565b6102d291906117e4565b815181106102e2576102e2611798565b602002602001018181525050610364565b87878381811061030557610305611798565b905060200201602081019061031a91906117c7565b73ffffffffffffffffffffffffffffffffffffffff1631838261033d8886611752565b61034791906117e4565b8151811061035757610357611798565b6020026020010181815250505b60010161021f565b50600101610214565b5095945050505050565b600073ffffffffffffffffffffffffffffffffffffffff82163b1561051f576040805173ffffffffffffffffffffffffffffffffffffffff85811660248084019190915283518084039091018152604490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a0823100000000000000000000000000000000000000000000000000000000179052915184926000929084169161043191906117f7565b600060405180830381855afa9150503d806000811461046c576040519150601f19603f3d011682016040523d82523d6000602084013e610471565b606091505b505090508015610514576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301528316906370a0823190602401602060405180830381865afa1580156104e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061050b9190611826565b92505050610523565b600092505050610523565b5060005b92915050565b606082610562576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008367ffffffffffffffff81111561057d5761057d611769565b6040519080825280602002602001820160405280156105a6578160200160208202803683370190505b50905060005b848110156103755760008473ffffffffffffffffffffffffffffffffffffffff166349b8a932898989868181106105e5576105e5611798565b905061022002016040518363ffffffff1660e01b81526004016106099291906118d9565b600060405180830381865afa158015610626573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261066c9190810190611a2a565b9050805160001461067e576000610681565b60015b83838151811061069357610693611798565b91151560209283029190910190910152506001016105ac565b606060008390036106e9576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008367ffffffffffffffff81111561070457610704611769565b60405190808252806020026020018201604052801561073757816020015b60608152602001906001900390816107225790505b50905060005b8481101561085c578373ffffffffffffffffffffffffffffffffffffffff1663a6dfcd4987878481811061077357610773611798565b905060200201602081019061078891906117c7565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401600060405180830381865afa1580156107f1573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526108379190810190611ac0565b82828151811061084957610849611798565b602090810291909101015260010161073d565b5090505b9392505050565b6060816108a0576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008267ffffffffffffffff8111156108bb576108bb611769565b6040519080825280602002602001820160405280156108e4578160200160208202803683370190505b50905060005b838110156103755761091e878787878581811061090957610909611798565b90506020020160208101906101a791906117c7565b82828151811061093057610930611798565b60209081029190910101526001016108ea565b60608261097c576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008367ffffffffffffffff81111561099757610997611769565b6040519080825280602002602001820160405280156109c0578160200160208202803683370190505b50905060005b848110156103755760008686838181106109e2576109e2611798565b905061016002018036038101906109f99190611b4f565b905060008573ffffffffffffffffffffffffffffffffffffffff1663b9cb01b08a846000015185602001518660400151876060015188608001518960c001518a60e001518b61010001518c61012001518d61014001516040518c63ffffffff1660e01b8152600401610ae79b9a9998979695949392919073ffffffffffffffffffffffffffffffffffffffff9b8c168152602081019a909a5260408a01989098529589166060890152938816608088015260a087019290925290951660c085015260e084019490945260ff939093166101008301526101208201929092526101408101919091526101600190565b600060405180830381865afa158015610b04573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610b4a9190810190611a2a565b90508051600014610b5c576000610b5f565b60015b848481518110610b7157610b71611798565b9115156020928302919091019091015250506001016109c6565b60606000859003610bc8576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b848314610c01576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008567ffffffffffffffff811115610c1c57610c1c611769565b604051908082528060200260200182016040528015610c45578160200160208202803683370190505b50905060005b86811015610d6d578373ffffffffffffffffffffffffffffffffffffffff16631647795e898984818110610c8157610c81611798565b9050602002016020810190610c9691906117c7565b888885818110610ca857610ca8611798565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b16815273ffffffffffffffffffffffffffffffffffffffff90941660048501526020029190910135602483015250604401602060405180830381865afa158015610d1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d439190611c01565b828281518110610d5557610d55611798565b91151560209283029190910190910152600101610c4b565b509695505050505050565b606081610db1576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008267ffffffffffffffff811115610dcc57610dcc611769565b604051908082528060200260200182016040528015610df5578160200160208202803683370190505b50905060005b8381101561085c576000858583818110610e1757610e17611798565b9050602002016020810190610e2c91906117c7565b73ffffffffffffffffffffffffffffffffffffffff1614610e8057610e5d868686848181106102a7576102a7611798565b828281518110610e6f57610e6f611798565b602002602001018181525050610eb7565b8573ffffffffffffffffffffffffffffffffffffffff1631828281518110610eaa57610eaa611798565b6020026020010181815250505b600101610dfb565b600073ffffffffffffffffffffffffffffffffffffffff82163b1561106f576040805173ffffffffffffffffffffffffffffffffffffffff868116602483015285811660448084019190915283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fdd62ed3e000000000000000000000000000000000000000000000000000000001790529151849260009290841691610f7991906117f7565b600060405180830381855afa9150503d8060008114610fb4576040519150601f19603f3d011682016040523d82523d6000602084013e610fb9565b606091505b505090508015611064576040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152868116602483015283169063dd62ed3e90604401602060405180830381865afa158015611037573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105b9190611826565b92505050610860565b600092505050610860565b5060009392505050565b606060006110878684611752565b67ffffffffffffffff81111561109f5761109f611769565b6040519080825280602002602001820160405280156110c8578160200160208202803683370190505b50905060005b86811015610d6d5760005b848110156111575761111e8989848181106110f6576110f6611798565b905060200201602081019061110b91906117c7565b8888888581811061090957610909611798565b838261112a8886611752565b61113491906117e4565b8151811061114457611144611798565b60209081029190910101526001016110d9565b506001016110ce565b60008083601f84011261117257600080fd5b50813567ffffffffffffffff81111561118a57600080fd5b6020830191508360208260051b85010111156111a557600080fd5b9250929050565b600080600080604085870312156111c257600080fd5b843567ffffffffffffffff808211156111da57600080fd5b6111e688838901611160565b909650945060208701359150808211156111ff57600080fd5b5061120c87828801611160565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b8181101561125057835183529284019291840191600101611234565b50909695505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461127e57600080fd5b50565b803561128c8161125c565b919050565b600080604083850312156112a457600080fd5b82356112af8161125c565b915060208301356112bf8161125c565b809150509250929050565b600080600080606085870312156112e057600080fd5b84356112eb8161125c565b9350602085013567ffffffffffffffff8082111561130857600080fd5b818701915087601f83011261131c57600080fd5b81358181111561132b57600080fd5b8860206102208302850101111561134157600080fd5b602083019550809450505050604085013561135b8161125c565b939692955090935050565b6020808252825182820181905260009190848201906040850190845b81811015611250578351151583529284019291840191600101611382565b6000806000604084860312156113b557600080fd5b833567ffffffffffffffff8111156113cc57600080fd5b6113d886828701611160565b90945092505060208401356113ec8161125c565b809150509250925092565b6000602080830181845280855180835260408601915060408160051b87010192508387016000805b838110156114a7578886037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0018552825180518088529088019088880190845b8181101561149157835173ffffffffffffffffffffffffffffffffffffffff168352928a0192918a019160010161145f565b509097505050938601939186019160010161141f565b509398975050505050505050565b600080600080606085870312156114cb57600080fd5b84356114d68161125c565b935060208501356114e68161125c565b9250604085013567ffffffffffffffff81111561150257600080fd5b61120c87828801611160565b6000806000806060858703121561152457600080fd5b843561152f8161125c565b9350602085013567ffffffffffffffff8082111561154c57600080fd5b818701915087601f83011261156057600080fd5b81358181111561156f57600080fd5b8860206101608302850101111561134157600080fd5b60008060008060006060868803121561159d57600080fd5b853567ffffffffffffffff808211156115b557600080fd5b6115c189838a01611160565b909750955060208801359150808211156115da57600080fd5b506115e788828901611160565b90945092505060408601356115fb8161125c565b809150509295509295909350565b60008060006040848603121561161e57600080fd5b83356116298161125c565b9250602084013567ffffffffffffffff81111561164557600080fd5b61165186828701611160565b9497909650939450505050565b60008060006060848603121561167357600080fd5b833561167e8161125c565b9250602084013561168e8161125c565b915060408401356113ec8161125c565b6000806000806000606086880312156116b657600080fd5b853567ffffffffffffffff808211156116ce57600080fd5b6116da89838a01611160565b9097509550602088013591506116ef8261125c565b9093506040870135908082111561170557600080fd5b5061171288828901611160565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808202811582820484141761052357610523611723565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156117d957600080fd5b81356108608161125c565b8082018082111561052357610523611723565b6000825160005b8181101561181857602081860181015185830152016117fe565b506000920191825250919050565b60006020828403121561183857600080fd5b5051919050565b803561184a8161125c565b73ffffffffffffffffffffffffffffffffffffffff90811683526020820135906118738261125c565b16602083015260408101357fffffffff0000000000000000000000000000000000000000000000000000000081168082146118ad57600080fd5b60408401525060608181013590830152608090810135910152565b803560ff8116811461128c57600080fd5b60006102408201905073ffffffffffffffffffffffffffffffffffffffff8085168352833560208401526020840135604084015261191d606084016040860161183f565b61192e610100840160e0860161183f565b61018084013561193d8161125c565b166101a0838101919091528301356101c0808401919091526119608482016118c8565b90506101e060ff821681850152610200915080850135828501525080840135610220840152509392505050565b604051610160810167ffffffffffffffff811182821017156119b1576119b1611769565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156119fe576119fe611769565b604052919050565b600067ffffffffffffffff821115611a2057611a20611769565b5060051b60200190565b60006020808385031215611a3d57600080fd5b825167ffffffffffffffff811115611a5457600080fd5b8301601f81018513611a6557600080fd5b8051611a78611a7382611a06565b6119b7565b81815260059190911b82018301908381019087831115611a9757600080fd5b928401925b82841015611ab557835182529284019290840190611a9c565b979650505050505050565b60006020808385031215611ad357600080fd5b825167ffffffffffffffff811115611aea57600080fd5b8301601f81018513611afb57600080fd5b8051611b09611a7382611a06565b81815260059190911b82018301908381019087831115611b2857600080fd5b928401925b82841015611ab5578351611b408161125c565b82529284019290840190611b2d565b60006101608284031215611b6257600080fd5b611b6a61198d565b8235815260208301356020820152611b8460408401611281565b6040820152611b9560608401611281565b606082015260808301356080820152611bb060a08401611281565b60a0820152611bc160c08401611281565b60c082015260e083013560e0820152610100611bde8185016118c8565b908201526101208381013590820152610140928301359281019290925250919050565b600060208284031215611c1357600080fd5b8151801515811461086057600080fdfea2646970667358221220927d0199d54c98894549aa24916c4d5ff18c42b05e8a851d973d01ab71d7b78064736f6c63430008170033
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106100be5760003560e01c80634c6305801161007657806377a7d9681161005b57806377a7d968146101865780638e8758d814610199578063df8344fe146101ac57600080fd5b80634c6305801461016057806359c94ce41461017357600080fd5b806317c958eb116100a757806317c958eb1461010d5780631e61290d1461012d578063366dff9c1461014d57600080fd5b806301afd5f3146100c35780631049334f146100ec575b600080fd5b6100d66100d13660046111ac565b6101bf565b6040516100e39190611218565b60405180910390f35b6100ff6100fa366004611291565b61037f565b6040519081526020016100e3565b61012061011b3660046112ca565b610529565b6040516100e39190611366565b61014061013b3660046113a0565b6106ac565b6040516100e391906113f7565b6100d661015b3660046114b5565b610867565b61012061016e36600461150e565b610943565b610120610181366004611585565b610b8b565b6100d6610194366004611609565b610d78565b6100ff6101a736600461165e565b610ebf565b6100d66101ba36600461169e565b611079565b606060006101cd8584611752565b67ffffffffffffffff8111156101e5576101e5611769565b60405190808252806020026020018201604052801561020e578160200160208202803683370190505b50905060005b858110156103755760005b8481101561036c57600086868381811061023b5761023b611798565b905060200201602081019061025091906117c7565b73ffffffffffffffffffffffffffffffffffffffff16146102f3576102bc88888481811061028057610280611798565b905060200201602081019061029591906117c7565b8787848181106102a7576102a7611798565b90506020020160208101906100fa91906117c7565b83826102c88886611752565b6102d291906117e4565b815181106102e2576102e2611798565b602002602001018181525050610364565b87878381811061030557610305611798565b905060200201602081019061031a91906117c7565b73ffffffffffffffffffffffffffffffffffffffff1631838261033d8886611752565b61034791906117e4565b8151811061035757610357611798565b6020026020010181815250505b60010161021f565b50600101610214565b5095945050505050565b600073ffffffffffffffffffffffffffffffffffffffff82163b1561051f576040805173ffffffffffffffffffffffffffffffffffffffff85811660248084019190915283518084039091018152604490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a0823100000000000000000000000000000000000000000000000000000000179052915184926000929084169161043191906117f7565b600060405180830381855afa9150503d806000811461046c576040519150601f19603f3d011682016040523d82523d6000602084013e610471565b606091505b505090508015610514576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301528316906370a0823190602401602060405180830381865afa1580156104e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061050b9190611826565b92505050610523565b600092505050610523565b5060005b92915050565b606082610562576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008367ffffffffffffffff81111561057d5761057d611769565b6040519080825280602002602001820160405280156105a6578160200160208202803683370190505b50905060005b848110156103755760008473ffffffffffffffffffffffffffffffffffffffff166349b8a932898989868181106105e5576105e5611798565b905061022002016040518363ffffffff1660e01b81526004016106099291906118d9565b600060405180830381865afa158015610626573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261066c9190810190611a2a565b9050805160001461067e576000610681565b60015b83838151811061069357610693611798565b91151560209283029190910190910152506001016105ac565b606060008390036106e9576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008367ffffffffffffffff81111561070457610704611769565b60405190808252806020026020018201604052801561073757816020015b60608152602001906001900390816107225790505b50905060005b8481101561085c578373ffffffffffffffffffffffffffffffffffffffff1663a6dfcd4987878481811061077357610773611798565b905060200201602081019061078891906117c7565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401600060405180830381865afa1580156107f1573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526108379190810190611ac0565b82828151811061084957610849611798565b602090810291909101015260010161073d565b5090505b9392505050565b6060816108a0576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008267ffffffffffffffff8111156108bb576108bb611769565b6040519080825280602002602001820160405280156108e4578160200160208202803683370190505b50905060005b838110156103755761091e878787878581811061090957610909611798565b90506020020160208101906101a791906117c7565b82828151811061093057610930611798565b60209081029190910101526001016108ea565b60608261097c576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008367ffffffffffffffff81111561099757610997611769565b6040519080825280602002602001820160405280156109c0578160200160208202803683370190505b50905060005b848110156103755760008686838181106109e2576109e2611798565b905061016002018036038101906109f99190611b4f565b905060008573ffffffffffffffffffffffffffffffffffffffff1663b9cb01b08a846000015185602001518660400151876060015188608001518960c001518a60e001518b61010001518c61012001518d61014001516040518c63ffffffff1660e01b8152600401610ae79b9a9998979695949392919073ffffffffffffffffffffffffffffffffffffffff9b8c168152602081019a909a5260408a01989098529589166060890152938816608088015260a087019290925290951660c085015260e084019490945260ff939093166101008301526101208201929092526101408101919091526101600190565b600060405180830381865afa158015610b04573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610b4a9190810190611a2a565b90508051600014610b5c576000610b5f565b60015b848481518110610b7157610b71611798565b9115156020928302919091019091015250506001016109c6565b60606000859003610bc8576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b848314610c01576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008567ffffffffffffffff811115610c1c57610c1c611769565b604051908082528060200260200182016040528015610c45578160200160208202803683370190505b50905060005b86811015610d6d578373ffffffffffffffffffffffffffffffffffffffff16631647795e898984818110610c8157610c81611798565b9050602002016020810190610c9691906117c7565b888885818110610ca857610ca8611798565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b16815273ffffffffffffffffffffffffffffffffffffffff90941660048501526020029190910135602483015250604401602060405180830381865afa158015610d1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d439190611c01565b828281518110610d5557610d55611798565b91151560209283029190910190910152600101610c4b565b509695505050505050565b606081610db1576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008267ffffffffffffffff811115610dcc57610dcc611769565b604051908082528060200260200182016040528015610df5578160200160208202803683370190505b50905060005b8381101561085c576000858583818110610e1757610e17611798565b9050602002016020810190610e2c91906117c7565b73ffffffffffffffffffffffffffffffffffffffff1614610e8057610e5d868686848181106102a7576102a7611798565b828281518110610e6f57610e6f611798565b602002602001018181525050610eb7565b8573ffffffffffffffffffffffffffffffffffffffff1631828281518110610eaa57610eaa611798565b6020026020010181815250505b600101610dfb565b600073ffffffffffffffffffffffffffffffffffffffff82163b1561106f576040805173ffffffffffffffffffffffffffffffffffffffff868116602483015285811660448084019190915283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fdd62ed3e000000000000000000000000000000000000000000000000000000001790529151849260009290841691610f7991906117f7565b600060405180830381855afa9150503d8060008114610fb4576040519150601f19603f3d011682016040523d82523d6000602084013e610fb9565b606091505b505090508015611064576040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152868116602483015283169063dd62ed3e90604401602060405180830381865afa158015611037573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105b9190611826565b92505050610860565b600092505050610860565b5060009392505050565b606060006110878684611752565b67ffffffffffffffff81111561109f5761109f611769565b6040519080825280602002602001820160405280156110c8578160200160208202803683370190505b50905060005b86811015610d6d5760005b848110156111575761111e8989848181106110f6576110f6611798565b905060200201602081019061110b91906117c7565b8888888581811061090957610909611798565b838261112a8886611752565b61113491906117e4565b8151811061114457611144611798565b60209081029190910101526001016110d9565b506001016110ce565b60008083601f84011261117257600080fd5b50813567ffffffffffffffff81111561118a57600080fd5b6020830191508360208260051b85010111156111a557600080fd5b9250929050565b600080600080604085870312156111c257600080fd5b843567ffffffffffffffff808211156111da57600080fd5b6111e688838901611160565b909650945060208701359150808211156111ff57600080fd5b5061120c87828801611160565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b8181101561125057835183529284019291840191600101611234565b50909695505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461127e57600080fd5b50565b803561128c8161125c565b919050565b600080604083850312156112a457600080fd5b82356112af8161125c565b915060208301356112bf8161125c565b809150509250929050565b600080600080606085870312156112e057600080fd5b84356112eb8161125c565b9350602085013567ffffffffffffffff8082111561130857600080fd5b818701915087601f83011261131c57600080fd5b81358181111561132b57600080fd5b8860206102208302850101111561134157600080fd5b602083019550809450505050604085013561135b8161125c565b939692955090935050565b6020808252825182820181905260009190848201906040850190845b81811015611250578351151583529284019291840191600101611382565b6000806000604084860312156113b557600080fd5b833567ffffffffffffffff8111156113cc57600080fd5b6113d886828701611160565b90945092505060208401356113ec8161125c565b809150509250925092565b6000602080830181845280855180835260408601915060408160051b87010192508387016000805b838110156114a7578886037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0018552825180518088529088019088880190845b8181101561149157835173ffffffffffffffffffffffffffffffffffffffff168352928a0192918a019160010161145f565b509097505050938601939186019160010161141f565b509398975050505050505050565b600080600080606085870312156114cb57600080fd5b84356114d68161125c565b935060208501356114e68161125c565b9250604085013567ffffffffffffffff81111561150257600080fd5b61120c87828801611160565b6000806000806060858703121561152457600080fd5b843561152f8161125c565b9350602085013567ffffffffffffffff8082111561154c57600080fd5b818701915087601f83011261156057600080fd5b81358181111561156f57600080fd5b8860206101608302850101111561134157600080fd5b60008060008060006060868803121561159d57600080fd5b853567ffffffffffffffff808211156115b557600080fd5b6115c189838a01611160565b909750955060208801359150808211156115da57600080fd5b506115e788828901611160565b90945092505060408601356115fb8161125c565b809150509295509295909350565b60008060006040848603121561161e57600080fd5b83356116298161125c565b9250602084013567ffffffffffffffff81111561164557600080fd5b61165186828701611160565b9497909650939450505050565b60008060006060848603121561167357600080fd5b833561167e8161125c565b9250602084013561168e8161125c565b915060408401356113ec8161125c565b6000806000806000606086880312156116b657600080fd5b853567ffffffffffffffff808211156116ce57600080fd5b6116da89838a01611160565b9097509550602088013591506116ef8261125c565b9093506040870135908082111561170557600080fd5b5061171288828901611160565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808202811582820484141761052357610523611723565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156117d957600080fd5b81356108608161125c565b8082018082111561052357610523611723565b6000825160005b8181101561181857602081860181015185830152016117fe565b506000920191825250919050565b60006020828403121561183857600080fd5b5051919050565b803561184a8161125c565b73ffffffffffffffffffffffffffffffffffffffff90811683526020820135906118738261125c565b16602083015260408101357fffffffff0000000000000000000000000000000000000000000000000000000081168082146118ad57600080fd5b60408401525060608181013590830152608090810135910152565b803560ff8116811461128c57600080fd5b60006102408201905073ffffffffffffffffffffffffffffffffffffffff8085168352833560208401526020840135604084015261191d606084016040860161183f565b61192e610100840160e0860161183f565b61018084013561193d8161125c565b166101a0838101919091528301356101c0808401919091526119608482016118c8565b90506101e060ff821681850152610200915080850135828501525080840135610220840152509392505050565b604051610160810167ffffffffffffffff811182821017156119b1576119b1611769565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156119fe576119fe611769565b604052919050565b600067ffffffffffffffff821115611a2057611a20611769565b5060051b60200190565b60006020808385031215611a3d57600080fd5b825167ffffffffffffffff811115611a5457600080fd5b8301601f81018513611a6557600080fd5b8051611a78611a7382611a06565b6119b7565b81815260059190911b82018301908381019087831115611a9757600080fd5b928401925b82841015611ab557835182529284019290840190611a9c565b979650505050505050565b60006020808385031215611ad357600080fd5b825167ffffffffffffffff811115611aea57600080fd5b8301601f81018513611afb57600080fd5b8051611b09611a7382611a06565b81815260059190911b82018301908381019087831115611b2857600080fd5b928401925b82841015611ab5578351611b408161125c565b82529284019290840190611b2d565b60006101608284031215611b6257600080fd5b611b6a61198d565b8235815260208301356020820152611b8460408401611281565b6040820152611b9560608401611281565b606082015260808301356080820152611bb060a08401611281565b60a0820152611bc160c08401611281565b60c082015260e083013560e0820152610100611bde8185016118c8565b908201526101208381013590820152610140928301359281019290925250919050565b600060208284031215611c1357600080fd5b8151801515811461086057600080fdfea2646970667358221220927d0199d54c98894549aa24916c4d5ff18c42b05e8a851d973d01ab71d7b78064736f6c63430008170033