Multichain Info
Latest 25 from a total of 1,078,129 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| 0x13d79a0b | 397996567 | 47 secs ago | IN | 0 ETH | 0.00000383 | ||||
| 0x13d79a0b | 397996469 | 1 min ago | IN | 0 ETH | 0.00000244 | ||||
| 0x13d79a0b | 397996434 | 1 min ago | IN | 0 ETH | 0.00000296 | ||||
| 0x13d79a0b | 397996401 | 1 min ago | IN | 0 ETH | 0.00000256 | ||||
| 0x13d79a0b | 397996212 | 2 mins ago | IN | 0 ETH | 0.00000278 | ||||
| 0x13d79a0b | 397996180 | 2 mins ago | IN | 0 ETH | 0.00000244 | ||||
| 0x13d79a0b | 397995820 | 3 mins ago | IN | 0 ETH | 0.00000864 | ||||
| 0x13d79a0b | 397995566 | 4 mins ago | IN | 0 ETH | 0.00000335 | ||||
| 0x13d79a0b | 397995275 | 6 mins ago | IN | 0 ETH | 0.00000413 | ||||
| 0x13d79a0b | 397995239 | 6 mins ago | IN | 0 ETH | 0.00000233 | ||||
| 0x13d79a0b | 397995238 | 6 mins ago | IN | 0 ETH | 0.00000267 | ||||
| 0x13d79a0b | 397995203 | 6 mins ago | IN | 0 ETH | 0.00000296 | ||||
| 0x13d79a0b | 397994900 | 7 mins ago | IN | 0 ETH | 0.00000429 | ||||
| 0x13d79a0b | 397994511 | 9 mins ago | IN | 0 ETH | 0.00000234 | ||||
| 0x13d79a0b | 397994128 | 10 mins ago | IN | 0 ETH | 0.0000024 | ||||
| 0x13d79a0b | 397994061 | 11 mins ago | IN | 0 ETH | 0.00000235 | ||||
| 0x13d79a0b | 397993965 | 11 mins ago | IN | 0 ETH | 0.00000303 | ||||
| 0x13d79a0b | 397993641 | 12 mins ago | IN | 0 ETH | 0.00003788 | ||||
| 0x13d79a0b | 397993541 | 13 mins ago | IN | 0 ETH | 0.00000276 | ||||
| 0x13d79a0b | 397993406 | 13 mins ago | IN | 0 ETH | 0.00000274 | ||||
| 0x13d79a0b | 397993373 | 14 mins ago | IN | 0 ETH | 0.00000241 | ||||
| 0x13d79a0b | 397993308 | 14 mins ago | IN | 0 ETH | 0.00000276 | ||||
| 0x13d79a0b | 397993177 | 14 mins ago | IN | 0 ETH | 0.00000235 | ||||
| 0x13d79a0b | 397993112 | 15 mins ago | IN | 0 ETH | 0.00000276 | ||||
| 0x13d79a0b | 397992941 | 15 mins ago | IN | 0 ETH | 0.00000428 |
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 397985676 | 46 mins ago | 0.00943776 ETH | ||||
| 397985676 | 46 mins ago | 0.00943777 ETH | ||||
| 397982034 | 1 hr ago | 0.00479069 ETH | ||||
| 397982034 | 1 hr ago | 0.00479069 ETH | ||||
| 397980936 | 1 hr ago | 0.98064685 ETH | ||||
| 397980936 | 1 hr ago | 0.98064685 ETH | ||||
| 397971153 | 1 hr ago | 0.29111563 ETH | ||||
| 397971153 | 1 hr ago | 0.29111563 ETH | ||||
| 397951635 | 3 hrs ago | 0.33 ETH | ||||
| 397951635 | 3 hrs ago | 0.33 ETH | ||||
| 397950150 | 3 hrs ago | 0.1227841 ETH | ||||
| 397950150 | 3 hrs ago | 0.1227841 ETH | ||||
| 397945797 | 3 hrs ago | 0.04563823 ETH | ||||
| 397945797 | 3 hrs ago | 0.04563823 ETH | ||||
| 397943360 | 3 hrs ago | 0.00086753 ETH | ||||
| 397943360 | 3 hrs ago | 0.00086753 ETH | ||||
| 397943167 | 3 hrs ago | 20.54913167 ETH | ||||
| 397943167 | 3 hrs ago | 20.54913167 ETH | ||||
| 397942860 | 3 hrs ago | 0.95869696 ETH | ||||
| 397942860 | 3 hrs ago | 0.95869696 ETH | ||||
| 397936286 | 4 hrs ago | 0.01489469 ETH | ||||
| 397936286 | 4 hrs ago | 0.01489469 ETH | ||||
| 397931583 | 4 hrs ago | 0.00087051 ETH | ||||
| 397931583 | 4 hrs ago | 0.00087051 ETH | ||||
| 397930646 | 4 hrs ago | 0.01736388 ETH |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
GPv2Settlement
Compiler Version
v0.7.6+commit.7338295f
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.7.6;
pragma abicoder v2;
import "./GPv2VaultRelayer.sol";
import "./interfaces/GPv2Authentication.sol";
import "./interfaces/IERC20.sol";
import "./interfaces/IVault.sol";
import "./libraries/GPv2Interaction.sol";
import "./libraries/GPv2Order.sol";
import "./libraries/GPv2Trade.sol";
import "./libraries/GPv2Transfer.sol";
import "./libraries/SafeCast.sol";
import "./libraries/SafeMath.sol";
import "./mixins/GPv2Signing.sol";
import "./mixins/ReentrancyGuard.sol";
import "./mixins/StorageAccessible.sol";
/// @title Gnosis Protocol v2 Settlement Contract
/// @author Gnosis Developers
contract GPv2Settlement is GPv2Signing, ReentrancyGuard, StorageAccessible {
using GPv2Order for bytes;
using GPv2Transfer for IVault;
using SafeCast for int256;
using SafeCast for uint256;
using SafeMath for uint256;
/// @dev The authenticator is used to determine who can call the settle function.
/// That is, only authorised solvers have the ability to invoke settlements.
/// Any valid authenticator implements an isSolver method called by the onlySolver
/// modifier below.
GPv2Authentication public immutable authenticator;
/// @dev The Balancer Vault the protocol uses for managing user funds.
IVault public immutable vault;
/// @dev The Balancer Vault relayer which can interact on behalf of users.
/// This contract is created during deployment
GPv2VaultRelayer public immutable vaultRelayer;
/// @dev Map each user order by UID to the amount that has been filled so
/// far. If this amount is larger than or equal to the amount traded in the
/// order (amount sold for sell orders, amount bought for buy orders) then
/// the order cannot be traded anymore. If the order is fill or kill, then
/// this value is only used to determine whether the order has already been
/// executed.
mapping(bytes => uint256) public filledAmount;
/// @dev Event emitted for each executed trade.
event Trade(
address indexed owner,
IERC20 sellToken,
IERC20 buyToken,
uint256 sellAmount,
uint256 buyAmount,
uint256 feeAmount,
bytes orderUid
);
/// @dev Event emitted for each executed interaction.
///
/// For gas effeciency, only the interaction calldata selector (first 4
/// bytes) is included in the event. For interactions without calldata or
/// whose calldata is shorter than 4 bytes, the selector will be `0`.
event Interaction(address indexed target, uint256 value, bytes4 selector);
/// @dev Event emitted when a settlement complets
event Settlement(address indexed solver);
/// @dev Event emitted when an order is invalidated.
event OrderInvalidated(address indexed owner, bytes orderUid);
constructor(GPv2Authentication authenticator_, IVault vault_) {
authenticator = authenticator_;
vault = vault_;
vaultRelayer = new GPv2VaultRelayer(vault_);
}
// solhint-disable-next-line no-empty-blocks
receive() external payable {
// NOTE: Include an empty receive function so that the settlement
// contract can receive Ether from contract interactions.
}
/// @dev This modifier is called by settle function to block any non-listed
/// senders from settling batches.
modifier onlySolver {
require(authenticator.isSolver(msg.sender), "GPv2: not a solver");
_;
}
/// @dev Modifier to ensure that an external function is only callable as a
/// settlement interaction.
modifier onlyInteraction {
require(address(this) == msg.sender, "GPv2: not an interaction");
_;
}
/// @dev Settle the specified orders at a clearing price. Note that it is
/// the responsibility of the caller to ensure that all GPv2 invariants are
/// upheld for the input settlement, otherwise this call will revert.
/// Namely:
/// - All orders are valid and signed
/// - Accounts have sufficient balance and approval.
/// - Settlement contract has sufficient balance to execute trades. Note
/// this implies that the accumulated fees held in the contract can also
/// be used for settlement. This is OK since:
/// - Solvers need to be authorized
/// - Misbehaving solvers will be slashed for abusing accumulated fees for
/// settlement
/// - Critically, user orders are entirely protected
///
/// @param tokens An array of ERC20 tokens to be traded in the settlement.
/// Trades encode tokens as indices into this array.
/// @param clearingPrices An array of clearing prices where the `i`-th price
/// is for the `i`-th token in the [`tokens`] array.
/// @param trades Trades for signed orders.
/// @param interactions Smart contract interactions split into three
/// separate lists to be run before the settlement, during the settlement
/// and after the settlement respectively.
function settle(
IERC20[] calldata tokens,
uint256[] calldata clearingPrices,
GPv2Trade.Data[] calldata trades,
GPv2Interaction.Data[][3] calldata interactions
) external nonReentrant onlySolver {
executeInteractions(interactions[0]);
(
GPv2Transfer.Data[] memory inTransfers,
GPv2Transfer.Data[] memory outTransfers
) = computeTradeExecutions(tokens, clearingPrices, trades);
vaultRelayer.transferFromAccounts(inTransfers);
executeInteractions(interactions[1]);
vault.transferToAccounts(outTransfers);
executeInteractions(interactions[2]);
emit Settlement(msg.sender);
}
/// @dev Settle an order directly against Balancer V2 pools.
///
/// @param swaps The Balancer V2 swap steps to use for trading.
/// @param tokens An array of ERC20 tokens to be traded in the settlement.
/// Swaps and the trade encode tokens as indices into this array.
/// @param trade The trade to match directly against Balancer liquidity. The
/// order will always be fully executed, so the trade's `executedAmount`
/// field is used to represent a swap limit amount.
function swap(
IVault.BatchSwapStep[] calldata swaps,
IERC20[] calldata tokens,
GPv2Trade.Data calldata trade
) external nonReentrant onlySolver {
RecoveredOrder memory recoveredOrder = allocateRecoveredOrder();
GPv2Order.Data memory order = recoveredOrder.data;
recoverOrderFromTrade(recoveredOrder, tokens, trade);
IVault.SwapKind kind =
order.kind == GPv2Order.KIND_SELL
? IVault.SwapKind.GIVEN_IN
: IVault.SwapKind.GIVEN_OUT;
IVault.FundManagement memory funds;
funds.sender = recoveredOrder.owner;
funds.fromInternalBalance =
order.sellTokenBalance == GPv2Order.BALANCE_INTERNAL;
funds.recipient = payable(recoveredOrder.receiver);
funds.toInternalBalance =
order.buyTokenBalance == GPv2Order.BALANCE_INTERNAL;
int256[] memory limits = new int256[](tokens.length);
uint256 limitAmount = trade.executedAmount;
// NOTE: Array allocation initializes elements to 0, so we only need to
// set the limits we care about. This ensures that the swap will respect
// the order's limit price.
if (order.kind == GPv2Order.KIND_SELL) {
require(limitAmount >= order.buyAmount, "GPv2: limit too low");
limits[trade.sellTokenIndex] = order.sellAmount.toInt256();
limits[trade.buyTokenIndex] = -limitAmount.toInt256();
} else {
require(limitAmount <= order.sellAmount, "GPv2: limit too high");
limits[trade.sellTokenIndex] = limitAmount.toInt256();
limits[trade.buyTokenIndex] = -order.buyAmount.toInt256();
}
GPv2Transfer.Data memory feeTransfer;
feeTransfer.account = recoveredOrder.owner;
feeTransfer.token = order.sellToken;
feeTransfer.amount = order.feeAmount;
feeTransfer.balance = order.sellTokenBalance;
int256[] memory tokenDeltas =
vaultRelayer.batchSwapWithFee(
kind,
swaps,
tokens,
funds,
limits,
// NOTE: Specify a deadline to ensure that an expire order
// cannot be used to trade.
order.validTo,
feeTransfer
);
bytes memory orderUid = recoveredOrder.uid;
uint256 executedSellAmount =
tokenDeltas[trade.sellTokenIndex].toUint256();
uint256 executedBuyAmount =
(-tokenDeltas[trade.buyTokenIndex]).toUint256();
// NOTE: Check that the orders were completely filled and update their
// filled amounts to avoid replaying them. The limit price and order
// validity have already been verified when executing the swap through
// the `limit` and `deadline` parameters.
require(filledAmount[orderUid] == 0, "GPv2: order filled");
if (order.kind == GPv2Order.KIND_SELL) {
require(
executedSellAmount == order.sellAmount,
"GPv2: sell amount not respected"
);
filledAmount[orderUid] = order.sellAmount;
} else {
require(
executedBuyAmount == order.buyAmount,
"GPv2: buy amount not respected"
);
filledAmount[orderUid] = order.buyAmount;
}
emit Trade(
recoveredOrder.owner,
order.sellToken,
order.buyToken,
executedSellAmount,
executedBuyAmount,
order.feeAmount,
orderUid
);
emit Settlement(msg.sender);
}
/// @dev Invalidate onchain an order that has been signed offline.
///
/// @param orderUid The unique identifier of the order that is to be made
/// invalid after calling this function. The user that created the order
/// must be the the sender of this message. See [`extractOrderUidParams`]
/// for details on orderUid.
function invalidateOrder(bytes calldata orderUid) external {
(, address owner, ) = orderUid.extractOrderUidParams();
require(owner == msg.sender, "GPv2: caller does not own order");
filledAmount[orderUid] = uint256(-1);
emit OrderInvalidated(owner, orderUid);
}
/// @dev Free storage from the filled amounts of **expired** orders to claim
/// a gas refund. This method can only be called as an interaction.
///
/// @param orderUids The unique identifiers of the expired order to free
/// storage for.
function freeFilledAmountStorage(bytes[] calldata orderUids)
external
onlyInteraction
{
freeOrderStorage(filledAmount, orderUids);
}
/// @dev Free storage from the pre signatures of **expired** orders to claim
/// a gas refund. This method can only be called as an interaction.
///
/// @param orderUids The unique identifiers of the expired order to free
/// storage for.
function freePreSignatureStorage(bytes[] calldata orderUids)
external
onlyInteraction
{
freeOrderStorage(preSignature, orderUids);
}
/// @dev Process all trades one at a time returning the computed net in and
/// out transfers for the trades.
///
/// This method reverts if processing of any single trade fails. See
/// [`computeTradeExecution`] for more details.
///
/// @param tokens An array of ERC20 tokens to be traded in the settlement.
/// @param clearingPrices An array of token clearing prices.
/// @param trades Trades for signed orders.
/// @return inTransfers Array of in transfers of executed sell amounts.
/// @return outTransfers Array of out transfers of executed buy amounts.
function computeTradeExecutions(
IERC20[] calldata tokens,
uint256[] calldata clearingPrices,
GPv2Trade.Data[] calldata trades
)
internal
returns (
GPv2Transfer.Data[] memory inTransfers,
GPv2Transfer.Data[] memory outTransfers
)
{
RecoveredOrder memory recoveredOrder = allocateRecoveredOrder();
inTransfers = new GPv2Transfer.Data[](trades.length);
outTransfers = new GPv2Transfer.Data[](trades.length);
for (uint256 i = 0; i < trades.length; i++) {
GPv2Trade.Data calldata trade = trades[i];
recoverOrderFromTrade(recoveredOrder, tokens, trade);
computeTradeExecution(
recoveredOrder,
clearingPrices[trade.sellTokenIndex],
clearingPrices[trade.buyTokenIndex],
trade.executedAmount,
inTransfers[i],
outTransfers[i]
);
}
}
/// @dev Compute the in and out transfer amounts for a single trade.
/// This function reverts if:
/// - The order has expired
/// - The order's limit price is not respected
/// - The order gets over-filled
/// - The fee discount is larger than the executed fee
///
/// @param recoveredOrder The recovered order to process.
/// @param sellPrice The price of the order's sell token.
/// @param buyPrice The price of the order's buy token.
/// @param executedAmount The portion of the order to execute. This will be
/// ignored for fill-or-kill orders.
/// @param inTransfer Memory location for computed executed sell amount
/// transfer.
/// @param outTransfer Memory location for computed executed buy amount
/// transfer.
function computeTradeExecution(
RecoveredOrder memory recoveredOrder,
uint256 sellPrice,
uint256 buyPrice,
uint256 executedAmount,
GPv2Transfer.Data memory inTransfer,
GPv2Transfer.Data memory outTransfer
) internal {
GPv2Order.Data memory order = recoveredOrder.data;
bytes memory orderUid = recoveredOrder.uid;
// solhint-disable-next-line not-rely-on-time
require(order.validTo >= block.timestamp, "GPv2: order expired");
// NOTE: The following computation is derived from the equation:
// ```
// amount_x * price_x = amount_y * price_y
// ```
// Intuitively, if a chocolate bar is 0,50€ and a beer is 4€, 1 beer
// is roughly worth 8 chocolate bars (`1 * 4 = 8 * 0.5`). From this
// equation, we can derive:
// - The limit price for selling `x` and buying `y` is respected iff
// ```
// limit_x * price_x >= limit_y * price_y
// ```
// - The executed amount of token `y` given some amount of `x` and
// clearing prices is:
// ```
// amount_y = amount_x * price_x / price_y
// ```
require(
order.sellAmount.mul(sellPrice) >= order.buyAmount.mul(buyPrice),
"GPv2: limit price not respected"
);
uint256 executedSellAmount;
uint256 executedBuyAmount;
uint256 executedFeeAmount;
uint256 currentFilledAmount;
if (order.kind == GPv2Order.KIND_SELL) {
if (order.partiallyFillable) {
executedSellAmount = executedAmount;
executedFeeAmount = order.feeAmount.mul(executedSellAmount).div(
order.sellAmount
);
} else {
executedSellAmount = order.sellAmount;
executedFeeAmount = order.feeAmount;
}
executedBuyAmount = executedSellAmount.mul(sellPrice).ceilDiv(
buyPrice
);
currentFilledAmount = filledAmount[orderUid].add(
executedSellAmount
);
require(
currentFilledAmount <= order.sellAmount,
"GPv2: order filled"
);
} else {
if (order.partiallyFillable) {
executedBuyAmount = executedAmount;
executedFeeAmount = order.feeAmount.mul(executedBuyAmount).div(
order.buyAmount
);
} else {
executedBuyAmount = order.buyAmount;
executedFeeAmount = order.feeAmount;
}
executedSellAmount = executedBuyAmount.mul(buyPrice).div(sellPrice);
currentFilledAmount = filledAmount[orderUid].add(executedBuyAmount);
require(
currentFilledAmount <= order.buyAmount,
"GPv2: order filled"
);
}
executedSellAmount = executedSellAmount.add(executedFeeAmount);
filledAmount[orderUid] = currentFilledAmount;
emit Trade(
recoveredOrder.owner,
order.sellToken,
order.buyToken,
executedSellAmount,
executedBuyAmount,
executedFeeAmount,
orderUid
);
inTransfer.account = recoveredOrder.owner;
inTransfer.token = order.sellToken;
inTransfer.amount = executedSellAmount;
inTransfer.balance = order.sellTokenBalance;
outTransfer.account = recoveredOrder.receiver;
outTransfer.token = order.buyToken;
outTransfer.amount = executedBuyAmount;
outTransfer.balance = order.buyTokenBalance;
}
/// @dev Execute a list of arbitrary contract calls from this contract.
/// @param interactions The list of interactions to execute.
function executeInteractions(GPv2Interaction.Data[] calldata interactions)
internal
{
for (uint256 i; i < interactions.length; i++) {
GPv2Interaction.Data calldata interaction = interactions[i];
// To prevent possible attack on user funds, we explicitly disable
// any interactions with the vault relayer contract.
require(
interaction.target != address(vaultRelayer),
"GPv2: forbidden interaction"
);
GPv2Interaction.execute(interaction);
emit Interaction(
interaction.target,
interaction.value,
GPv2Interaction.selector(interaction)
);
}
}
/// @dev Claims refund for the specified storage and order UIDs.
///
/// This method reverts if any of the orders are still valid.
///
/// @param orderUids Order refund data for freeing storage.
/// @param orderStorage Order storage mapped on a UID.
function freeOrderStorage(
mapping(bytes => uint256) storage orderStorage,
bytes[] calldata orderUids
) internal {
for (uint256 i = 0; i < orderUids.length; i++) {
bytes calldata orderUid = orderUids[i];
(, , uint32 validTo) = orderUid.extractOrderUidParams();
// solhint-disable-next-line not-rely-on-time
require(validTo < block.timestamp, "GPv2: order still valid");
orderStorage[orderUid] = 0;
}
}
}// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.7.6;
pragma abicoder v2;
import "./interfaces/IERC20.sol";
import "./interfaces/IVault.sol";
import "./libraries/GPv2Transfer.sol";
/// @title Gnosis Protocol v2 Vault Relayer Contract
/// @author Gnosis Developers
contract GPv2VaultRelayer {
using GPv2Transfer for IVault;
/// @dev The creator of the contract which has special permissions. This
/// value is set at creation time and cannot change.
address private immutable creator;
/// @dev The vault this relayer is for.
IVault private immutable vault;
constructor(IVault vault_) {
creator = msg.sender;
vault = vault_;
}
/// @dev Modifier that ensures that a function can only be called by the
/// creator of this contract.
modifier onlyCreator {
require(msg.sender == creator, "GPv2: not creator");
_;
}
/// @dev Transfers all sell amounts for the executed trades from their
/// owners to the caller.
///
/// This function reverts if:
/// - The caller is not the creator of the vault relayer
/// - Any ERC20 transfer fails
///
/// @param transfers The transfers to execute.
function transferFromAccounts(GPv2Transfer.Data[] calldata transfers)
external
onlyCreator
{
vault.transferFromAccounts(transfers, msg.sender);
}
/// @dev Performs a Balancer batched swap on behalf of a user and sends a
/// fee to the caller.
///
/// This function reverts if:
/// - The caller is not the creator of the vault relayer
/// - The swap fails
/// - The fee transfer fails
///
/// @param kind The Balancer swap kind, this can either be `GIVEN_IN` for
/// sell orders or `GIVEN_OUT` for buy orders.
/// @param swaps The swaps to perform.
/// @param tokens The tokens for the swaps. Swaps encode to and from tokens
/// as indices into this array.
/// @param funds The fund management settings, specifying the user the swap
/// is being performed for as well as the recipient of the proceeds.
/// @param limits Swap limits for encoding limit prices.
/// @param deadline The deadline for the swap.
/// @param feeTransfer The transfer data for the caller fee.
/// @return tokenDeltas The executed swap amounts.
function batchSwapWithFee(
IVault.SwapKind kind,
IVault.BatchSwapStep[] calldata swaps,
IERC20[] memory tokens,
IVault.FundManagement memory funds,
int256[] memory limits,
uint256 deadline,
GPv2Transfer.Data calldata feeTransfer
) external onlyCreator returns (int256[] memory tokenDeltas) {
tokenDeltas = vault.batchSwap(
kind,
swaps,
tokens,
funds,
limits,
deadline
);
vault.fastTransferFromAccount(feeTransfer, msg.sender);
}
}// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.7.6;
/// @title Gnosis Protocol v2 Authentication Interface
/// @author Gnosis Developers
interface GPv2Authentication {
/// @dev determines whether the provided address is an authenticated solver.
/// @param prospectiveSolver the address of prospective solver.
/// @return true when prospectiveSolver is an authenticated solver, otherwise false.
function isSolver(address prospectiveSolver) external view returns (bool);
}// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.7.6;
library GPv2EIP1271 {
/// @dev Value returned by a call to `isValidSignature` if the signature
/// was verified successfully. The value is defined in EIP-1271 as:
/// bytes4(keccak256("isValidSignature(bytes32,bytes)"))
bytes4 internal constant MAGICVALUE = 0x1626ba7e;
}
/// @title EIP1271 Interface
/// @dev Standardized interface for an implementation of smart contract
/// signatures as described in EIP-1271. The code that follows is identical to
/// the code in the standard with the exception of formatting and syntax
/// changes to adapt the code to our Solidity version.
interface EIP1271Verifier {
/// @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
///
/// MUST return the bytes4 magic value 0x1626ba7e when function passes.
/// MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for
/// solc > 0.5)
/// MUST allow external calls
///
function isValidSignature(bytes32 _hash, bytes memory _signature)
external
view
returns (bytes4 magicValue);
}// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin contracts with minor modifications:
// - Modified Solidity version
// - Formatted code
// - Added `name`, `symbol` and `decimals` function declarations
// <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/token/ERC20/IERC20.sol>
pragma solidity ^0.7.6;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the number of decimals the token uses.
*/
function decimals() external view returns (uint8);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount)
external
returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender)
external
view
returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.7.6;
pragma abicoder v2;
import "./IERC20.sol";
/**
* @dev Minimal interface for the Vault core contract only containing methods
* used by Gnosis Protocol V2. Original source:
* <https://github.com/balancer-labs/balancer-core-v2/blob/v1.0.0/contracts/vault/interfaces/IVault.sol>
*/
interface IVault {
// Internal Balance
//
// Users can deposit tokens into the Vault, where they are allocated to their Internal Balance, and later
// transferred or withdrawn. It can also be used as a source of tokens when joining Pools, as a destination
// when exiting them, and as either when performing swaps. This usage of Internal Balance results in greatly reduced
// gas costs when compared to relying on plain ERC20 transfers, leading to large savings for frequent users.
//
// Internal Balance management features batching, which means a single contract call can be used to perform multiple
// operations of different kinds, with different senders and recipients, at once.
/**
* @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)
* and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as
* it lets integrators reuse a user's Vault allowance.
*
* For each operation, if the caller is not `sender`, it must be an authorized relayer for them.
*/
function manageUserBalance(UserBalanceOp[] memory ops) external payable;
/**
* @dev Data for `manageUserBalance` operations, which include the possibility for ETH to be sent and received
without manual WETH wrapping or unwrapping.
*/
struct UserBalanceOp {
UserBalanceOpKind kind;
IERC20 asset;
uint256 amount;
address sender;
address payable recipient;
}
// There are four possible operations in `manageUserBalance`:
//
// - DEPOSIT_INTERNAL
// Increases the Internal Balance of the `recipient` account by transferring tokens from the corresponding
// `sender`. The sender must have allowed the Vault to use their tokens via `IERC20.approve()`.
//
// ETH can be used by passing the ETH sentinel value as the asset and forwarding ETH in the call: it will be wrapped
// and deposited as WETH. Any ETH amount remaining will be sent back to the caller (not the sender, which is
// relevant for relayers).
//
// Emits an `InternalBalanceChanged` event.
//
//
// - WITHDRAW_INTERNAL
// Decreases the Internal Balance of the `sender` account by transferring tokens to the `recipient`.
//
// ETH can be used by passing the ETH sentinel value as the asset. This will deduct WETH instead, unwrap it and send
// it to the recipient as ETH.
//
// Emits an `InternalBalanceChanged` event.
//
//
// - TRANSFER_INTERNAL
// Transfers tokens from the Internal Balance of the `sender` account to the Internal Balance of `recipient`.
//
// Reverts if the ETH sentinel value is passed.
//
// Emits an `InternalBalanceChanged` event.
//
//
// - TRANSFER_EXTERNAL
// Transfers tokens from `sender` to `recipient`, using the Vault's ERC20 allowance. This is typically used by
// relayers, as it lets them reuse a user's Vault allowance.
//
// Reverts if the ETH sentinel value is passed.
//
// Emits an `ExternalBalanceTransfer` event.
enum UserBalanceOpKind {
DEPOSIT_INTERNAL,
WITHDRAW_INTERNAL,
TRANSFER_INTERNAL,
TRANSFER_EXTERNAL
}
// Swaps
//
// Users can swap tokens with Pools by calling the `swap` and `batchSwap` functions. To do this,
// they need not trust Pool contracts in any way: all security checks are made by the Vault. They must however be
// aware of the Pools' pricing algorithms in order to estimate the prices Pools will quote.
//
// The `swap` function executes a single swap, while `batchSwap` can perform multiple swaps in sequence.
// In each individual swap, tokens of one kind are sent from the sender to the Pool (this is the 'token in'),
// and tokens of another kind are sent from the Pool to the recipient in exchange (this is the 'token out').
// More complex swaps, such as one token in to multiple tokens out can be achieved by batching together
// individual swaps.
//
// There are two swap kinds:
// - 'given in' swaps, where the amount of tokens in (sent to the Pool) is known, and the Pool determines (via the
// `onSwap` hook) the amount of tokens out (to send to the recipient).
// - 'given out' swaps, where the amount of tokens out (received from the Pool) is known, and the Pool determines
// (via the `onSwap` hook) the amount of tokens in (to receive from the sender).
//
// Additionally, it is possible to chain swaps using a placeholder input amount, which the Vault replaces with
// the calculated output of the previous swap. If the previous swap was 'given in', this will be the calculated
// tokenOut amount. If the previous swap was 'given out', it will use the calculated tokenIn amount. These extended
// swaps are known as 'multihop' swaps, since they 'hop' through a number of intermediate tokens before arriving at
// the final intended token.
//
// In all cases, tokens are only transferred in and out of the Vault (or withdrawn from and deposited into Internal
// Balance) after all individual swaps have been completed, and the net token balance change computed. This makes
// certain swap patterns, such as multihops, or swaps that interact with the same token pair in multiple Pools, cost
// much less gas than they would otherwise.
//
// It also means that under certain conditions it is possible to perform arbitrage by swapping with multiple
// Pools in a way that results in net token movement out of the Vault (profit), with no tokens being sent in (only
// updating the Pool's internal accounting).
//
// To protect users from front-running or the market changing rapidly, they supply a list of 'limits' for each token
// involved in the swap, where either the maximum number of tokens to send (by passing a positive value) or the
// minimum amount of tokens to receive (by passing a negative value) is specified.
//
// Additionally, a 'deadline' timestamp can also be provided, forcing the swap to fail if it occurs after
// this point in time (e.g. if the transaction failed to be included in a block promptly).
//
// If interacting with Pools that hold WETH, it is possible to both send and receive ETH directly: the Vault will do
// the wrapping and unwrapping. To enable this mechanism, the IAsset sentinel value (the zero address) must be
// passed in the `assets` array instead of the WETH address. Note that it is possible to combine ETH and WETH in the
// same swap. Any excess ETH will be sent back to the caller (not the sender, which is relevant for relayers).
//
// Finally, Internal Balance can be used when either sending or receiving tokens.
enum SwapKind {GIVEN_IN, GIVEN_OUT}
/**
* @dev Performs a swap with a single Pool.
*
* If the swap is 'given in' (the number of tokens to send to the Pool is known), it returns the amount of tokens
* taken from the Pool, which must be greater than or equal to `limit`.
*
* If the swap is 'given out' (the number of tokens to take from the Pool is known), it returns the amount of tokens
* sent to the Pool, which must be less than or equal to `limit`.
*
* Internal Balance usage and the recipient are determined by the `funds` struct.
*
* Emits a `Swap` event.
*/
function swap(
SingleSwap memory singleSwap,
FundManagement memory funds,
uint256 limit,
uint256 deadline
) external payable returns (uint256);
/**
* @dev Data for a single swap executed by `swap`. `amount` is either `amountIn` or `amountOut` depending on
* the `kind` value.
*
* `assetIn` and `assetOut` are either token addresses, or the IAsset sentinel value for ETH (the zero address).
* Note that Pools never interact with ETH directly: it will be wrapped to or unwrapped from WETH by the Vault.
*
* The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be
* used to extend swap behavior.
*/
struct SingleSwap {
bytes32 poolId;
SwapKind kind;
IERC20 assetIn;
IERC20 assetOut;
uint256 amount;
bytes userData;
}
/**
* @dev Performs a series of swaps with one or multiple Pools. In each individual swap, the caller determines either
* the amount of tokens sent to or received from the Pool, depending on the `kind` value.
*
* Returns an array with the net Vault asset balance deltas. Positive amounts represent tokens (or ETH) sent to the
* Vault, and negative amounts represent tokens (or ETH) sent by the Vault. Each delta corresponds to the asset at
* the same index in the `assets` array.
*
* Swaps are executed sequentially, in the order specified by the `swaps` array. Each array element describes a
* Pool, the token to be sent to this Pool, the token to receive from it, and an amount that is either `amountIn` or
* `amountOut` depending on the swap kind.
*
* Multihop swaps can be executed by passing an `amount` value of zero for a swap. This will cause the amount in/out
* of the previous swap to be used as the amount in for the current one. In a 'given in' swap, 'tokenIn' must equal
* the previous swap's `tokenOut`. For a 'given out' swap, `tokenOut` must equal the previous swap's `tokenIn`.
*
* The `assets` array contains the addresses of all assets involved in the swaps. These are either token addresses,
* or the IAsset sentinel value for ETH (the zero address). Each entry in the `swaps` array specifies tokens in and
* out by referencing an index in `assets`. Note that Pools never interact with ETH directly: it will be wrapped to
* or unwrapped from WETH by the Vault.
*
* Internal Balance usage, sender, and recipient are determined by the `funds` struct. The `limits` array specifies
* the minimum or maximum amount of each token the vault is allowed to transfer.
*
* `batchSwap` can be used to make a single swap, like `swap` does, but doing so requires more gas than the
* equivalent `swap` call.
*
* Emits `Swap` events.
*/
function batchSwap(
SwapKind kind,
BatchSwapStep[] memory swaps,
IERC20[] memory assets,
FundManagement memory funds,
int256[] memory limits,
uint256 deadline
) external payable returns (int256[] memory);
/**
* @dev Data for each individual swap executed by `batchSwap`. The asset in and out fields are indexes into the
* `assets` array passed to that function, and ETH assets are converted to WETH.
*
* If `amount` is zero, the multihop mechanism is used to determine the actual amount based on the amount in/out
* from the previous swap, depending on the swap kind.
*
* The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be
* used to extend swap behavior.
*/
struct BatchSwapStep {
bytes32 poolId;
uint256 assetInIndex;
uint256 assetOutIndex;
uint256 amount;
bytes userData;
}
/**
* @dev All tokens in a swap are either sent from the `sender` account to the Vault, or from the Vault to the
* `recipient` account.
*
* If the caller is not `sender`, it must be an authorized relayer for them.
*
* If `fromInternalBalance` is true, the `sender`'s Internal Balance will be preferred, performing an ERC20
* transfer for the difference between the requested amount and the User's Internal Balance (if any). The `sender`
* must have allowed the Vault to use their tokens via `IERC20.approve()`. This matches the behavior of
* `joinPool`.
*
* If `toInternalBalance` is true, tokens will be deposited to `recipient`'s internal balance instead of
* transferred. This matches the behavior of `exitPool`.
*
* Note that ETH cannot be deposited to or withdrawn from Internal Balance: attempting to do so will trigger a
* revert.
*/
struct FundManagement {
address sender;
bool fromInternalBalance;
address payable recipient;
bool toInternalBalance;
}
}// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.7.6;
/// @title Gnosis Protocol v2 Interaction Library
/// @author Gnosis Developers
library GPv2Interaction {
/// @dev Interaction data for performing arbitrary contract interactions.
/// Submitted to [`GPv2Settlement.settle`] for code execution.
struct Data {
address target;
uint256 value;
bytes callData;
}
/// @dev Execute an arbitrary contract interaction.
///
/// @param interaction Interaction data.
function execute(Data calldata interaction) internal {
address target = interaction.target;
uint256 value = interaction.value;
bytes calldata callData = interaction.callData;
// NOTE: Use assembly to call the interaction instead of a low level
// call for two reasons:
// - We don't want to copy the return data, since we discard it for
// interactions.
// - Solidity will under certain conditions generate code to copy input
// calldata twice to memory (the second being a "memcopy loop").
// <https://github.com/gnosis/gp-v2-contracts/pull/417#issuecomment-775091258>
// solhint-disable-next-line no-inline-assembly
assembly {
let freeMemoryPointer := mload(0x40)
calldatacopy(freeMemoryPointer, callData.offset, callData.length)
if iszero(
call(
gas(),
target,
value,
freeMemoryPointer,
callData.length,
0,
0
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
/// @dev Extracts the Solidity ABI selector for the specified interaction.
///
/// @param interaction Interaction data.
/// @return result The 4 byte function selector of the call encoded in
/// this interaction.
function selector(Data calldata interaction)
internal
pure
returns (bytes4 result)
{
bytes calldata callData = interaction.callData;
if (callData.length >= 4) {
// NOTE: Read the first word of the interaction's calldata. The
// value does not need to be shifted since `bytesN` values are left
// aligned, and the value does not need to be masked since masking
// occurs when the value is accessed and not stored:
// <https://docs.soliditylang.org/en/v0.7.6/abi-spec.html#encoding-of-indexed-event-parameters>
// <https://docs.soliditylang.org/en/v0.7.6/assembly.html#access-to-external-variables-functions-and-libraries>
// solhint-disable-next-line no-inline-assembly
assembly {
result := calldataload(callData.offset)
}
}
}
}// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.7.6;
import "../interfaces/IERC20.sol";
/// @title Gnosis Protocol v2 Order Library
/// @author Gnosis Developers
library GPv2Order {
/// @dev The complete data for a Gnosis Protocol order. This struct contains
/// all order parameters that are signed for submitting to GP.
struct Data {
IERC20 sellToken;
IERC20 buyToken;
address receiver;
uint256 sellAmount;
uint256 buyAmount;
uint32 validTo;
bytes32 appData;
uint256 feeAmount;
bytes32 kind;
bool partiallyFillable;
bytes32 sellTokenBalance;
bytes32 buyTokenBalance;
}
/// @dev The order EIP-712 type hash for the [`GPv2Order.Data`] struct.
///
/// This value is pre-computed from the following expression:
/// ```
/// keccak256(
/// "Order(" +
/// "address sellToken," +
/// "address buyToken," +
/// "address receiver," +
/// "uint256 sellAmount," +
/// "uint256 buyAmount," +
/// "uint32 validTo," +
/// "bytes32 appData," +
/// "uint256 feeAmount," +
/// "string kind," +
/// "bool partiallyFillable" +
/// "string sellTokenBalance" +
/// "string buyTokenBalance" +
/// ")"
/// )
/// ```
bytes32 internal constant TYPE_HASH =
hex"d5a25ba2e97094ad7d83dc28a6572da797d6b3e7fc6663bd93efb789fc17e489";
/// @dev The marker value for a sell order for computing the order struct
/// hash. This allows the EIP-712 compatible wallets to display a
/// descriptive string for the order kind (instead of 0 or 1).
///
/// This value is pre-computed from the following expression:
/// ```
/// keccak256("sell")
/// ```
bytes32 internal constant KIND_SELL =
hex"f3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee346775";
/// @dev The OrderKind marker value for a buy order for computing the order
/// struct hash.
///
/// This value is pre-computed from the following expression:
/// ```
/// keccak256("buy")
/// ```
bytes32 internal constant KIND_BUY =
hex"6ed88e868af0a1983e3886d5f3e95a2fafbd6c3450bc229e27342283dc429ccc";
/// @dev The TokenBalance marker value for using direct ERC20 balances for
/// computing the order struct hash.
///
/// This value is pre-computed from the following expression:
/// ```
/// keccak256("erc20")
/// ```
bytes32 internal constant BALANCE_ERC20 =
hex"5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9";
/// @dev The TokenBalance marker value for using Balancer Vault external
/// balances (in order to re-use Vault ERC20 approvals) for computing the
/// order struct hash.
///
/// This value is pre-computed from the following expression:
/// ```
/// keccak256("external")
/// ```
bytes32 internal constant BALANCE_EXTERNAL =
hex"abee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea0632";
/// @dev The TokenBalance marker value for using Balancer Vault internal
/// balances for computing the order struct hash.
///
/// This value is pre-computed from the following expression:
/// ```
/// keccak256("internal")
/// ```
bytes32 internal constant BALANCE_INTERNAL =
hex"4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce";
/// @dev Marker address used to indicate that the receiver of the trade
/// proceeds should the owner of the order.
///
/// This is chosen to be `address(0)` for gas efficiency as it is expected
/// to be the most common case.
address internal constant RECEIVER_SAME_AS_OWNER = address(0);
/// @dev The byte length of an order unique identifier.
uint256 internal constant UID_LENGTH = 56;
/// @dev Returns the actual receiver for an order. This function checks
/// whether or not the [`receiver`] field uses the marker value to indicate
/// it is the same as the order owner.
///
/// @return receiver The actual receiver of trade proceeds.
function actualReceiver(Data memory order, address owner)
internal
pure
returns (address receiver)
{
if (order.receiver == RECEIVER_SAME_AS_OWNER) {
receiver = owner;
} else {
receiver = order.receiver;
}
}
/// @dev Return the EIP-712 signing hash for the specified order.
///
/// @param order The order to compute the EIP-712 signing hash for.
/// @param domainSeparator The EIP-712 domain separator to use.
/// @return orderDigest The 32 byte EIP-712 struct hash.
function hash(Data memory order, bytes32 domainSeparator)
internal
pure
returns (bytes32 orderDigest)
{
bytes32 structHash;
// NOTE: Compute the EIP-712 order struct hash in place. As suggested
// in the EIP proposal, noting that the order struct has 10 fields, and
// including the type hash `(12 + 1) * 32 = 416` bytes to hash.
// <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#rationale-for-encodedata>
// solhint-disable-next-line no-inline-assembly
assembly {
let dataStart := sub(order, 32)
let temp := mload(dataStart)
mstore(dataStart, TYPE_HASH)
structHash := keccak256(dataStart, 416)
mstore(dataStart, temp)
}
// NOTE: Now that we have the struct hash, compute the EIP-712 signing
// hash using scratch memory past the free memory pointer. The signing
// hash is computed from `"\x19\x01" || domainSeparator || structHash`.
// <https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html#layout-in-memory>
// <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#specification>
// solhint-disable-next-line no-inline-assembly
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, "\x19\x01")
mstore(add(freeMemoryPointer, 2), domainSeparator)
mstore(add(freeMemoryPointer, 34), structHash)
orderDigest := keccak256(freeMemoryPointer, 66)
}
}
/// @dev Packs order UID parameters into the specified memory location. The
/// result is equivalent to `abi.encodePacked(...)` with the difference that
/// it allows re-using the memory for packing the order UID.
///
/// This function reverts if the order UID buffer is not the correct size.
///
/// @param orderUid The buffer pack the order UID parameters into.
/// @param orderDigest The EIP-712 struct digest derived from the order
/// parameters.
/// @param owner The address of the user who owns this order.
/// @param validTo The epoch time at which the order will stop being valid.
function packOrderUidParams(
bytes memory orderUid,
bytes32 orderDigest,
address owner,
uint32 validTo
) internal pure {
require(orderUid.length == UID_LENGTH, "GPv2: uid buffer overflow");
// NOTE: Write the order UID to the allocated memory buffer. The order
// parameters are written to memory in **reverse order** as memory
// operations write 32-bytes at a time and we want to use a packed
// encoding. This means, for example, that after writing the value of
// `owner` to bytes `20:52`, writing the `orderDigest` to bytes `0:32`
// will **overwrite** bytes `20:32`. This is desirable as addresses are
// only 20 bytes and `20:32` should be `0`s:
//
// | 1111111111222222222233333333334444444444555555
// byte | 01234567890123456789012345678901234567890123456789012345
// -------+---------------------------------------------------------
// field | [.........orderDigest..........][......owner.......][vT]
// -------+---------------------------------------------------------
// mstore | [000000000000000000000000000.vT]
// | [00000000000.......owner.......]
// | [.........orderDigest..........]
//
// Additionally, since Solidity `bytes memory` are length prefixed,
// 32 needs to be added to all the offsets.
//
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(add(orderUid, 56), validTo)
mstore(add(orderUid, 52), owner)
mstore(add(orderUid, 32), orderDigest)
}
}
/// @dev Extracts specific order information from the standardized unique
/// order id of the protocol.
///
/// @param orderUid The unique identifier used to represent an order in
/// the protocol. This uid is the packed concatenation of the order digest,
/// the validTo order parameter and the address of the user who created the
/// order. It is used by the user to interface with the contract directly,
/// and not by calls that are triggered by the solvers.
/// @return orderDigest The EIP-712 signing digest derived from the order
/// parameters.
/// @return owner The address of the user who owns this order.
/// @return validTo The epoch time at which the order will stop being valid.
function extractOrderUidParams(bytes calldata orderUid)
internal
pure
returns (
bytes32 orderDigest,
address owner,
uint32 validTo
)
{
require(orderUid.length == UID_LENGTH, "GPv2: invalid uid");
// Use assembly to efficiently decode packed calldata.
// solhint-disable-next-line no-inline-assembly
assembly {
orderDigest := calldataload(orderUid.offset)
owner := shr(96, calldataload(add(orderUid.offset, 32)))
validTo := shr(224, calldataload(add(orderUid.offset, 52)))
}
}
}// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.7.6;
import "../interfaces/IERC20.sol";
/// @title Gnosis Protocol v2 Safe ERC20 Transfer Library
/// @author Gnosis Developers
/// @dev Gas-efficient version of Openzeppelin's SafeERC20 contract that notably
/// does not revert when calling a non-contract.
library GPv2SafeERC20 {
/// @dev Wrapper around a call to the ERC20 function `transfer` that reverts
/// also when the token returns `false`.
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
bytes4 selector_ = token.transfer.selector;
// solhint-disable-next-line no-inline-assembly
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, selector_)
mstore(
add(freeMemoryPointer, 4),
and(to, 0xffffffffffffffffffffffffffffffffffffffff)
)
mstore(add(freeMemoryPointer, 36), value)
if iszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
require(getLastTansferResult(token), "GPv2: failed transfer");
}
/// @dev Wrapper around a call to the ERC20 function `transferFrom` that
/// reverts also when the token returns `false`.
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
bytes4 selector_ = token.transferFrom.selector;
// solhint-disable-next-line no-inline-assembly
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, selector_)
mstore(
add(freeMemoryPointer, 4),
and(from, 0xffffffffffffffffffffffffffffffffffffffff)
)
mstore(
add(freeMemoryPointer, 36),
and(to, 0xffffffffffffffffffffffffffffffffffffffff)
)
mstore(add(freeMemoryPointer, 68), value)
if iszero(call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
require(getLastTansferResult(token), "GPv2: failed transferFrom");
}
/// @dev Verifies that the last return was a successful `transfer*` call.
/// This is done by checking that the return data is either empty, or
/// is a valid ABI encoded boolean.
function getLastTansferResult(IERC20 token)
private
view
returns (bool success)
{
// NOTE: Inspecting previous return data requires assembly. Note that
// we write the return data to memory 0 in the case where the return
// data size is 32, this is OK since the first 64 bytes of memory are
// reserved by Solidy as a scratch space that can be used within
// assembly blocks.
// <https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html>
// solhint-disable-next-line no-inline-assembly
assembly {
/// @dev Revert with an ABI encoded Solidity error with a message
/// that fits into 32-bytes.
///
/// An ABI encoded Solidity error has the following memory layout:
///
/// ------------+----------------------------------
/// byte range | value
/// ------------+----------------------------------
/// 0x00..0x04 | selector("Error(string)")
/// 0x04..0x24 | string offset (always 0x20)
/// 0x24..0x44 | string length
/// 0x44..0x64 | string value, padded to 32-bytes
function revertWithMessage(length, message) {
mstore(0x00, "\x08\xc3\x79\xa0")
mstore(0x04, 0x20)
mstore(0x24, length)
mstore(0x44, message)
revert(0x00, 0x64)
}
switch returndatasize()
// Non-standard ERC20 transfer without return.
case 0 {
// NOTE: When the return data size is 0, verify that there
// is code at the address. This is done in order to maintain
// compatibility with Solidity calling conventions.
// <https://docs.soliditylang.org/en/v0.7.6/control-structures.html#external-function-calls>
if iszero(extcodesize(token)) {
revertWithMessage(20, "GPv2: not a contract")
}
success := 1
}
// Standard ERC20 transfer returning boolean success value.
case 32 {
returndatacopy(0, 0, returndatasize())
// NOTE: For ABI encoding v1, any non-zero value is accepted
// as `true` for a boolean. In order to stay compatible with
// OpenZeppelin's `SafeERC20` library which is known to work
// with the existing ERC20 implementation we care about,
// make sure we return success for any non-zero return value
// from the `transfer*` call.
success := iszero(iszero(mload(0)))
}
default {
revertWithMessage(31, "GPv2: malformed transfer result")
}
}
}
}// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.7.6;
import "../interfaces/IERC20.sol";
import "../mixins/GPv2Signing.sol";
import "./GPv2Order.sol";
/// @title Gnosis Protocol v2 Trade Library.
/// @author Gnosis Developers
library GPv2Trade {
using GPv2Order for GPv2Order.Data;
using GPv2Order for bytes;
/// @dev A struct representing a trade to be executed as part a batch
/// settlement.
struct Data {
uint256 sellTokenIndex;
uint256 buyTokenIndex;
address receiver;
uint256 sellAmount;
uint256 buyAmount;
uint32 validTo;
bytes32 appData;
uint256 feeAmount;
uint256 flags;
uint256 executedAmount;
bytes signature;
}
/// @dev Extracts the order data and signing scheme for the specified trade.
///
/// @param trade The trade.
/// @param tokens The list of tokens included in the settlement. The token
/// indices in the trade parameters map to tokens in this array.
/// @param order The memory location to extract the order data to.
function extractOrder(
Data calldata trade,
IERC20[] calldata tokens,
GPv2Order.Data memory order
) internal pure returns (GPv2Signing.Scheme signingScheme) {
order.sellToken = tokens[trade.sellTokenIndex];
order.buyToken = tokens[trade.buyTokenIndex];
order.receiver = trade.receiver;
order.sellAmount = trade.sellAmount;
order.buyAmount = trade.buyAmount;
order.validTo = trade.validTo;
order.appData = trade.appData;
order.feeAmount = trade.feeAmount;
(
order.kind,
order.partiallyFillable,
order.sellTokenBalance,
order.buyTokenBalance,
signingScheme
) = extractFlags(trade.flags);
}
/// @dev Decodes trade flags.
///
/// Trade flags are used to tightly encode information on how to decode
/// an order. Examples that directly affect the structure of an order are
/// the kind of order (either a sell or a buy order) as well as whether the
/// order is partially fillable or if it is a "fill-or-kill" order. It also
/// encodes the signature scheme used to validate the order. As the most
/// likely values are fill-or-kill sell orders by an externally owned
/// account, the flags are chosen such that `0x00` represents this kind of
/// order. The flags byte uses the following format:
///
/// ```
/// bit | 31 ... | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
/// ----+----------+---+---+-------+---+---+
/// | reserved | * * | * | * * | * | * |
/// | | | | | | |
/// | | | | | | +---- order kind bit, 0 for a sell order
/// | | | | | | and 1 for a buy order
/// | | | | | |
/// | | | | | +-------- order fill bit, 0 for fill-or-kill
/// | | | | | and 1 for a partially fillable order
/// | | | | |
/// | | | +---+------------ use internal sell token balance bit:
/// | | | 0x: ERC20 token balance
/// | | | 10: external Balancer Vault balance
/// | | | 11: internal Balancer Vault balance
/// | | |
/// | | +-------------------- use buy token balance bit
/// | | 0: ERC20 token balance
/// | | 1: internal Balancer Vault balance
/// | |
/// +---+------------------------ signature scheme bits:
/// 00: EIP-712
/// 01: eth_sign
/// 10: EIP-1271
/// 11: pre_sign
/// ```
function extractFlags(uint256 flags)
internal
pure
returns (
bytes32 kind,
bool partiallyFillable,
bytes32 sellTokenBalance,
bytes32 buyTokenBalance,
GPv2Signing.Scheme signingScheme
)
{
if (flags & 0x01 == 0) {
kind = GPv2Order.KIND_SELL;
} else {
kind = GPv2Order.KIND_BUY;
}
partiallyFillable = flags & 0x02 != 0;
if (flags & 0x08 == 0) {
sellTokenBalance = GPv2Order.BALANCE_ERC20;
} else if (flags & 0x04 == 0) {
sellTokenBalance = GPv2Order.BALANCE_EXTERNAL;
} else {
sellTokenBalance = GPv2Order.BALANCE_INTERNAL;
}
if (flags & 0x10 == 0) {
buyTokenBalance = GPv2Order.BALANCE_ERC20;
} else {
buyTokenBalance = GPv2Order.BALANCE_INTERNAL;
}
// NOTE: Take advantage of the fact that Solidity will revert if the
// following expression does not produce a valid enum value. This means
// we check here that the leading reserved bits must be 0.
signingScheme = GPv2Signing.Scheme(flags >> 5);
}
}// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.7.6;
pragma abicoder v2;
import "../interfaces/IERC20.sol";
import "../interfaces/IVault.sol";
import "./GPv2Order.sol";
import "./GPv2SafeERC20.sol";
/// @title Gnosis Protocol v2 Transfers
/// @author Gnosis Developers
library GPv2Transfer {
using GPv2SafeERC20 for IERC20;
/// @dev Transfer data.
struct Data {
address account;
IERC20 token;
uint256 amount;
bytes32 balance;
}
/// @dev Ether marker address used to indicate an Ether transfer.
address internal constant BUY_ETH_ADDRESS =
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev Execute the specified transfer from the specified account to a
/// recipient. The recipient will either receive internal Vault balances or
/// ERC20 token balances depending on whether the account is using internal
/// balances or not.
///
/// This method is used for transferring fees to the settlement contract
/// when settling a single order directly with Balancer.
///
/// Note that this method is subtly different from `transferFromAccounts`
/// with a single transfer with respect to how it deals with internal
/// balances. Specifically, this method will perform an **internal balance
/// transfer to the settlement contract instead of a withdrawal to the
/// external balance of the settlement contract** for trades that specify
/// trading with internal balances. This is done as a gas optimization in
/// the single order "fast-path".
///
/// @param vault The Balancer vault to use.
/// @param transfer The transfer to perform specifying the sender account.
/// @param recipient The recipient for the transfer.
function fastTransferFromAccount(
IVault vault,
Data calldata transfer,
address recipient
) internal {
require(
address(transfer.token) != BUY_ETH_ADDRESS,
"GPv2: cannot transfer native ETH"
);
if (transfer.balance == GPv2Order.BALANCE_ERC20) {
transfer.token.safeTransferFrom(
transfer.account,
recipient,
transfer.amount
);
} else {
IVault.UserBalanceOp[] memory balanceOps =
new IVault.UserBalanceOp[](1);
IVault.UserBalanceOp memory balanceOp = balanceOps[0];
balanceOp.kind = transfer.balance == GPv2Order.BALANCE_EXTERNAL
? IVault.UserBalanceOpKind.TRANSFER_EXTERNAL
: IVault.UserBalanceOpKind.TRANSFER_INTERNAL;
balanceOp.asset = transfer.token;
balanceOp.amount = transfer.amount;
balanceOp.sender = transfer.account;
balanceOp.recipient = payable(recipient);
vault.manageUserBalance(balanceOps);
}
}
/// @dev Execute the specified transfers from the specified accounts to a
/// single recipient. The recipient will receive all transfers as ERC20
/// token balances, regardless of whether or not the accounts are using
/// internal Vault balances.
///
/// This method is used for accumulating user balances into the settlement
/// contract.
///
/// @param vault The Balancer vault to use.
/// @param transfers The batched transfers to perform specifying the
/// sender accounts.
/// @param recipient The single recipient for all the transfers.
function transferFromAccounts(
IVault vault,
Data[] calldata transfers,
address recipient
) internal {
// NOTE: Allocate buffer of Vault balance operations large enough to
// hold all GP transfers. This is done to avoid re-allocations (which
// are gas inefficient) while still allowing all transfers to be batched
// into a single Vault call.
IVault.UserBalanceOp[] memory balanceOps =
new IVault.UserBalanceOp[](transfers.length);
uint256 balanceOpCount = 0;
for (uint256 i = 0; i < transfers.length; i++) {
Data calldata transfer = transfers[i];
require(
address(transfer.token) != BUY_ETH_ADDRESS,
"GPv2: cannot transfer native ETH"
);
if (transfer.balance == GPv2Order.BALANCE_ERC20) {
transfer.token.safeTransferFrom(
transfer.account,
recipient,
transfer.amount
);
} else {
IVault.UserBalanceOp memory balanceOp =
balanceOps[balanceOpCount++];
balanceOp.kind = transfer.balance == GPv2Order.BALANCE_EXTERNAL
? IVault.UserBalanceOpKind.TRANSFER_EXTERNAL
: IVault.UserBalanceOpKind.WITHDRAW_INTERNAL;
balanceOp.asset = transfer.token;
balanceOp.amount = transfer.amount;
balanceOp.sender = transfer.account;
balanceOp.recipient = payable(recipient);
}
}
if (balanceOpCount > 0) {
truncateBalanceOpsArray(balanceOps, balanceOpCount);
vault.manageUserBalance(balanceOps);
}
}
/// @dev Execute the specified transfers to their respective accounts.
///
/// This method is used for paying out trade proceeds from the settlement
/// contract.
///
/// @param vault The Balancer vault to use.
/// @param transfers The batched transfers to perform.
function transferToAccounts(IVault vault, Data[] memory transfers)
internal
{
IVault.UserBalanceOp[] memory balanceOps =
new IVault.UserBalanceOp[](transfers.length);
uint256 balanceOpCount = 0;
for (uint256 i = 0; i < transfers.length; i++) {
Data memory transfer = transfers[i];
if (address(transfer.token) == BUY_ETH_ADDRESS) {
require(
transfer.balance != GPv2Order.BALANCE_INTERNAL,
"GPv2: unsupported internal ETH"
);
payable(transfer.account).transfer(transfer.amount);
} else if (transfer.balance == GPv2Order.BALANCE_ERC20) {
transfer.token.safeTransfer(transfer.account, transfer.amount);
} else {
IVault.UserBalanceOp memory balanceOp =
balanceOps[balanceOpCount++];
balanceOp.kind = IVault.UserBalanceOpKind.DEPOSIT_INTERNAL;
balanceOp.asset = transfer.token;
balanceOp.amount = transfer.amount;
balanceOp.sender = address(this);
balanceOp.recipient = payable(transfer.account);
}
}
if (balanceOpCount > 0) {
truncateBalanceOpsArray(balanceOps, balanceOpCount);
vault.manageUserBalance(balanceOps);
}
}
/// @dev Truncate a Vault balance operation array to its actual size.
///
/// This method **does not** check whether or not the new length is valid,
/// and specifying a size that is larger than the array's actual length is
/// undefined behaviour.
///
/// @param balanceOps The memory array of balance operations to truncate.
/// @param newLength The new length to set.
function truncateBalanceOpsArray(
IVault.UserBalanceOp[] memory balanceOps,
uint256 newLength
) private pure {
// NOTE: Truncate the vault transfers array to the specified length.
// This is done by setting the array's length which occupies the first
// word in memory pointed to by the `balanceOps` memory variable.
// <https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html>
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(balanceOps, newLength)
}
}
}// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin contracts with minor modifications:
// - Modified Solidity version
// - Formatted code
// - Shortened revert messages
// - Removed unused methods
// - Convert to `type(*).*` notation
// <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/utils/SafeCast.sol>
pragma solidity ^0.7.6;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: not positive");
return uint256(value);
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
require(
value <= uint256(type(int256).max),
"SafeCast: int256 overflow"
);
return int256(value);
}
}// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin contracts with minor modifications:
// - Modified Solidity version
// - Formatted code
// - Shortened some revert messages
// - Removed unused methods
// - Added `ceilDiv` method
// <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/math/SafeMath.sol>
pragma solidity ^0.7.6;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: mul overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by 0");
return a / b;
}
/**
* @dev Returns the ceiling integer division of two unsigned integers,
* reverting on division by zero. The result is rounded towards up the
* nearest integer, instead of truncating the fractional part.
*
* Requirements:
*
* - The divisor cannot be zero.
* - The sum of the dividend and divisor cannot overflow.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: ceiling division by 0");
return a / b + (a % b == 0 ? 0 : 1);
}
}// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity ^0.7.6;
import "../interfaces/GPv2EIP1271.sol";
import "../libraries/GPv2Order.sol";
import "../libraries/GPv2Trade.sol";
/// @title Gnosis Protocol v2 Signing Library.
/// @author Gnosis Developers
abstract contract GPv2Signing {
using GPv2Order for GPv2Order.Data;
using GPv2Order for bytes;
/// @dev Recovered trade data containing the extracted order and the
/// recovered owner address.
struct RecoveredOrder {
GPv2Order.Data data;
bytes uid;
address owner;
address receiver;
}
/// @dev Signing scheme used for recovery.
enum Scheme {Eip712, EthSign, Eip1271, PreSign}
/// @dev The EIP-712 domain type hash used for computing the domain
/// separator.
bytes32 private constant DOMAIN_TYPE_HASH =
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
/// @dev The EIP-712 domain name used for computing the domain separator.
bytes32 private constant DOMAIN_NAME = keccak256("Gnosis Protocol");
/// @dev The EIP-712 domain version used for computing the domain separator.
bytes32 private constant DOMAIN_VERSION = keccak256("v2");
/// @dev Marker value indicating an order is pre-signed.
uint256 private constant PRE_SIGNED =
uint256(keccak256("GPv2Signing.Scheme.PreSign"));
/// @dev The domain separator used for signing orders that gets mixed in
/// making signatures for different domains incompatible. This domain
/// separator is computed following the EIP-712 standard and has replay
/// protection mixed in so that signed orders are only valid for specific
/// GPv2 contracts.
bytes32 public immutable domainSeparator;
/// @dev Storage indicating whether or not an order has been signed by a
/// particular address.
mapping(bytes => uint256) public preSignature;
/// @dev Event that is emitted when an account either pre-signs an order or
/// revokes an existing pre-signature.
event PreSignature(address indexed owner, bytes orderUid, bool signed);
constructor() {
// NOTE: Currently, the only way to get the chain ID in solidity is
// using assembly.
uint256 chainId;
// solhint-disable-next-line no-inline-assembly
assembly {
chainId := chainid()
}
domainSeparator = keccak256(
abi.encode(
DOMAIN_TYPE_HASH,
DOMAIN_NAME,
DOMAIN_VERSION,
chainId,
address(this)
)
);
}
/// @dev Sets a presignature for the specified order UID.
///
/// @param orderUid The unique identifier of the order to pre-sign.
function setPreSignature(bytes calldata orderUid, bool signed) external {
(, address owner, ) = orderUid.extractOrderUidParams();
require(owner == msg.sender, "GPv2: cannot presign order");
if (signed) {
preSignature[orderUid] = PRE_SIGNED;
} else {
preSignature[orderUid] = 0;
}
emit PreSignature(owner, orderUid, signed);
}
/// @dev Returns an empty recovered order with a pre-allocated buffer for
/// packing the unique identifier.
///
/// @return recoveredOrder The empty recovered order data.
function allocateRecoveredOrder()
internal
pure
returns (RecoveredOrder memory recoveredOrder)
{
recoveredOrder.uid = new bytes(GPv2Order.UID_LENGTH);
}
/// @dev Extracts order data and recovers the signer from the specified
/// trade.
///
/// @param recoveredOrder Memory location used for writing the recovered order data.
/// @param tokens The list of tokens included in the settlement. The token
/// indices in the trade parameters map to tokens in this array.
/// @param trade The trade data to recover the order data from.
function recoverOrderFromTrade(
RecoveredOrder memory recoveredOrder,
IERC20[] calldata tokens,
GPv2Trade.Data calldata trade
) internal view {
GPv2Order.Data memory order = recoveredOrder.data;
Scheme signingScheme = GPv2Trade.extractOrder(trade, tokens, order);
(bytes32 orderDigest, address owner) =
recoverOrderSigner(order, signingScheme, trade.signature);
recoveredOrder.uid.packOrderUidParams(
orderDigest,
owner,
order.validTo
);
recoveredOrder.owner = owner;
recoveredOrder.receiver = order.actualReceiver(owner);
}
/// @dev The length of any signature from an externally owned account.
uint256 private constant ECDSA_SIGNATURE_LENGTH = 65;
/// @dev Recovers an order's signer from the specified order and signature.
///
/// @param order The order to recover a signature for.
/// @param signingScheme The signing scheme.
/// @param signature The signature bytes.
/// @return orderDigest The computed order hash.
/// @return owner The recovered address from the specified signature.
function recoverOrderSigner(
GPv2Order.Data memory order,
Scheme signingScheme,
bytes calldata signature
) internal view returns (bytes32 orderDigest, address owner) {
orderDigest = order.hash(domainSeparator);
if (signingScheme == Scheme.Eip712) {
owner = recoverEip712Signer(orderDigest, signature);
} else if (signingScheme == Scheme.EthSign) {
owner = recoverEthsignSigner(orderDigest, signature);
} else if (signingScheme == Scheme.Eip1271) {
owner = recoverEip1271Signer(orderDigest, signature);
} else {
// signingScheme == Scheme.PreSign
owner = recoverPreSigner(orderDigest, signature, order.validTo);
}
}
/// @dev Perform an ECDSA recover for the specified message and calldata
/// signature.
///
/// The signature is encoded by tighyly packing the following struct:
/// ```
/// struct EncodedSignature {
/// bytes32 r;
/// bytes32 s;
/// uint8 v;
/// }
/// ```
///
/// @param message The signed message.
/// @param encodedSignature The encoded signature.
function ecdsaRecover(bytes32 message, bytes calldata encodedSignature)
internal
pure
returns (address signer)
{
require(
encodedSignature.length == ECDSA_SIGNATURE_LENGTH,
"GPv2: malformed ecdsa signature"
);
bytes32 r;
bytes32 s;
uint8 v;
// NOTE: Use assembly to efficiently decode signature data.
// solhint-disable-next-line no-inline-assembly
assembly {
// r = uint256(encodedSignature[0:32])
r := calldataload(encodedSignature.offset)
// s = uint256(encodedSignature[32:64])
s := calldataload(add(encodedSignature.offset, 32))
// v = uint8(encodedSignature[64])
v := shr(248, calldataload(add(encodedSignature.offset, 64)))
}
signer = ecrecover(message, v, r, s);
require(signer != address(0), "GPv2: invalid ecdsa signature");
}
/// @dev Decodes signature bytes originating from an EIP-712-encoded
/// signature.
///
/// EIP-712 signs typed data. The specifications are described in the
/// related EIP (<https://eips.ethereum.org/EIPS/eip-712>).
///
/// EIP-712 signatures are encoded as standard ECDSA signatures as described
/// in the corresponding decoding function [`ecdsaRecover`].
///
/// @param orderDigest The EIP-712 signing digest derived from the order
/// parameters.
/// @param encodedSignature Calldata pointing to tightly packed signature
/// bytes.
/// @return owner The address of the signer.
function recoverEip712Signer(
bytes32 orderDigest,
bytes calldata encodedSignature
) internal pure returns (address owner) {
owner = ecdsaRecover(orderDigest, encodedSignature);
}
/// @dev Decodes signature bytes originating from the output of the eth_sign
/// RPC call.
///
/// The specifications are described in the Ethereum documentation
/// (<https://eth.wiki/json-rpc/API#eth_sign>).
///
/// eth_sign signatures are encoded as standard ECDSA signatures as
/// described in the corresponding decoding function
/// [`ecdsaRecover`].
///
/// @param orderDigest The EIP-712 signing digest derived from the order
/// parameters.
/// @param encodedSignature Calldata pointing to tightly packed signature
/// bytes.
/// @return owner The address of the signer.
function recoverEthsignSigner(
bytes32 orderDigest,
bytes calldata encodedSignature
) internal pure returns (address owner) {
// The signed message is encoded as:
// `"\x19Ethereum Signed Message:\n" || length || data`, where
// the length is a constant (32 bytes) and the data is defined as:
// `orderDigest`.
bytes32 ethsignDigest =
keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
orderDigest
)
);
owner = ecdsaRecover(ethsignDigest, encodedSignature);
}
/// @dev Verifies the input calldata as an EIP-1271 contract signature and
/// returns the address of the signer.
///
/// The encoded signature tightly packs the following struct:
///
/// ```
/// struct EncodedEip1271Signature {
/// address owner;
/// bytes signature;
/// }
/// ```
///
/// This function enforces that the encoded data stores enough bytes to
/// cover the full length of the decoded signature.
///
/// @param encodedSignature The encoded EIP-1271 signature.
/// @param orderDigest The EIP-712 signing digest derived from the order
/// parameters.
/// @return owner The address of the signer.
function recoverEip1271Signer(
bytes32 orderDigest,
bytes calldata encodedSignature
) internal view returns (address owner) {
// NOTE: Use assembly to read the verifier address from the encoded
// signature bytes.
// solhint-disable-next-line no-inline-assembly
assembly {
// owner = address(encodedSignature[0:20])
owner := shr(96, calldataload(encodedSignature.offset))
}
// NOTE: Configure prettier to ignore the following line as it causes
// a panic in the Solidity plugin.
// prettier-ignore
bytes calldata signature = encodedSignature[20:];
require(
EIP1271Verifier(owner).isValidSignature(orderDigest, signature) ==
GPv2EIP1271.MAGICVALUE,
"GPv2: invalid eip1271 signature"
);
}
/// @dev Verifies the order has been pre-signed. The signature is the
/// address of the signer of the order.
///
/// @param orderDigest The EIP-712 signing digest derived from the order
/// parameters.
/// @param encodedSignature The pre-sign signature reprenting the order UID.
/// @param validTo The order expiry timestamp.
/// @return owner The address of the signer.
function recoverPreSigner(
bytes32 orderDigest,
bytes calldata encodedSignature,
uint32 validTo
) internal view returns (address owner) {
require(encodedSignature.length == 20, "GPv2: malformed presignature");
// NOTE: Use assembly to read the owner address from the encoded
// signature bytes.
// solhint-disable-next-line no-inline-assembly
assembly {
// owner = address(encodedSignature[0:20])
owner := shr(96, calldataload(encodedSignature.offset))
}
bytes memory orderUid = new bytes(GPv2Order.UID_LENGTH);
orderUid.packOrderUidParams(orderDigest, owner, validTo);
require(
preSignature[orderUid] == PRE_SIGNED,
"GPv2: order not presigned"
);
}
}// SPDX-License-Identifier: MIT
// Vendored from OpenZeppelin contracts with minor modifications:
// - Modified Solidity version
// - Formatted code
// <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/utils/ReentrancyGuard.sol>
pragma solidity ^0.7.6;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}// SPDX-License-Identifier: LGPL-3.0-only
// Vendored from Gnosis utility contracts with minor modifications:
// - Modified Solidity version
// - Formatted code
// - Added linter directives to ignore low level call and assembly warnings
// <https://github.com/gnosis/util-contracts/blob/v3.1.0-solc-7/contracts/StorageAccessible.sol>
pragma solidity ^0.7.6;
/// @title ViewStorageAccessible - Interface on top of StorageAccessible base class to allow simulations from view functions
interface ViewStorageAccessible {
/**
* @dev Same as `simulateDelegatecall` on StorageAccessible. Marked as view so that it can be called from external contracts
* that want to run simulations from within view functions. Will revert if the invoked simulation attempts to change state.
*/
function simulateDelegatecall(
address targetContract,
bytes memory calldataPayload
) external view returns (bytes memory);
/**
* @dev Same as `getStorageAt` on StorageAccessible. This method allows reading aribtrary ranges of storage.
*/
function getStorageAt(uint256 offset, uint256 length)
external
view
returns (bytes memory);
}
/// @title StorageAccessible - generic base contract that allows callers to access all internal storage.
contract StorageAccessible {
/**
* @dev Reads `length` bytes of storage in the currents contract
* @param offset - the offset in the current contract's storage in words to start reading from
* @param length - the number of words (32 bytes) of data to read
* @return the bytes that were read.
*/
function getStorageAt(uint256 offset, uint256 length)
external
view
returns (bytes memory)
{
bytes memory result = new bytes(length * 32);
for (uint256 index = 0; index < length; index++) {
// solhint-disable-next-line no-inline-assembly
assembly {
let word := sload(add(offset, index))
mstore(add(add(result, 0x20), mul(index, 0x20)), word)
}
}
return result;
}
/**
* @dev Performs a delegetecall on a targetContract in the context of self.
* Internally reverts execution to avoid side effects (making it static). Catches revert and returns encoded result as bytes.
* @param targetContract Address of the contract containing the code to execute.
* @param calldataPayload Calldata that should be sent to the target contract (encoded method name and arguments).
*/
function simulateDelegatecall(
address targetContract,
bytes memory calldataPayload
) public returns (bytes memory response) {
bytes memory innerCall =
abi.encodeWithSelector(
this.simulateDelegatecallInternal.selector,
targetContract,
calldataPayload
);
// solhint-disable-next-line avoid-low-level-calls
(, response) = address(this).call(innerCall);
bool innerSuccess = response[response.length - 1] == 0x01;
setLength(response, response.length - 1);
if (innerSuccess) {
return response;
} else {
revertWith(response);
}
}
/**
* @dev Performs a delegetecall on a targetContract in the context of self.
* Internally reverts execution to avoid side effects (making it static). Returns encoded result as revert message
* concatenated with the success flag of the inner call as a last byte.
* @param targetContract Address of the contract containing the code to execute.
* @param calldataPayload Calldata that should be sent to the target contract (encoded method name and arguments).
*/
function simulateDelegatecallInternal(
address targetContract,
bytes memory calldataPayload
) external returns (bytes memory response) {
bool success;
// solhint-disable-next-line avoid-low-level-calls
(success, response) = targetContract.delegatecall(calldataPayload);
revertWith(abi.encodePacked(response, success));
}
function revertWith(bytes memory response) internal pure {
// solhint-disable-next-line no-inline-assembly
assembly {
revert(add(response, 0x20), mload(response))
}
}
function setLength(bytes memory buffer, uint256 length) internal pure {
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(buffer, length)
}
}
}{
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 1000000
},
"remappings": [],
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract GPv2Authentication","name":"authenticator_","type":"address"},{"internalType":"contract IVault","name":"vault_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"Interaction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"bytes","name":"orderUid","type":"bytes"}],"name":"OrderInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"bytes","name":"orderUid","type":"bytes"},{"indexed":false,"internalType":"bool","name":"signed","type":"bool"}],"name":"PreSignature","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"solver","type":"address"}],"name":"Settlement","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"sellToken","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"buyToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"sellAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"buyAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeAmount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"orderUid","type":"bytes"}],"name":"Trade","type":"event"},{"inputs":[],"name":"authenticator","outputs":[{"internalType":"contract GPv2Authentication","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"domainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"filledAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"orderUids","type":"bytes[]"}],"name":"freeFilledAmountStorage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"orderUids","type":"bytes[]"}],"name":"freePreSignatureStorage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"getStorageAt","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"orderUid","type":"bytes"}],"name":"invalidateOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"preSignature","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"orderUid","type":"bytes"},{"internalType":"bool","name":"signed","type":"bool"}],"name":"setPreSignature","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"clearingPrices","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"sellTokenIndex","type":"uint256"},{"internalType":"uint256","name":"buyTokenIndex","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"uint256","name":"buyAmount","type":"uint256"},{"internalType":"uint32","name":"validTo","type":"uint32"},{"internalType":"bytes32","name":"appData","type":"bytes32"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"},{"internalType":"uint256","name":"executedAmount","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct GPv2Trade.Data[]","name":"trades","type":"tuple[]"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct GPv2Interaction.Data[][3]","name":"interactions","type":"tuple[][3]"}],"name":"settle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"targetContract","type":"address"},{"internalType":"bytes","name":"calldataPayload","type":"bytes"}],"name":"simulateDelegatecall","outputs":[{"internalType":"bytes","name":"response","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"targetContract","type":"address"},{"internalType":"bytes","name":"calldataPayload","type":"bytes"}],"name":"simulateDelegatecallInternal","outputs":[{"internalType":"bytes","name":"response","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"poolId","type":"bytes32"},{"internalType":"uint256","name":"assetInIndex","type":"uint256"},{"internalType":"uint256","name":"assetOutIndex","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"userData","type":"bytes"}],"internalType":"struct IVault.BatchSwapStep[]","name":"swaps","type":"tuple[]"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"components":[{"internalType":"uint256","name":"sellTokenIndex","type":"uint256"},{"internalType":"uint256","name":"buyTokenIndex","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"uint256","name":"buyAmount","type":"uint256"},{"internalType":"uint32","name":"validTo","type":"uint32"},{"internalType":"bytes32","name":"appData","type":"bytes32"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"},{"internalType":"uint256","name":"executedAmount","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct GPv2Trade.Data","name":"trade","type":"tuple"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract IVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultRelayer","outputs":[{"internalType":"contract GPv2VaultRelayer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
6101006040523480156200001257600080fd5b50604051620053eb380380620053eb83398101604081905262000035916200015b565b604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527f6c85c0337eba1661327f94f3bf46c8a7f9311a563f4d5c948362567f5d8ed60c828401527ff9446b8e937d86f0bc87cac73923491692b123ca5f8761908494703758206adf606080840191909152466080808501919091523060a08086019190915285518086038201815260c09586019687905280519401939093209052600180556001600160601b031986821b811690925284901b16905281906200010a906200014d565b62000116919062000199565b604051809103906000f08015801562000133573d6000803e3d6000fd5b5060601b6001600160601b03191660e05250620001c69050565b61129e806200414d83390190565b600080604083850312156200016e578182fd5b82516200017b81620001ad565b60208401519092506200018e81620001ad565b809150509250929050565b6001600160a01b0391909116815260200190565b6001600160a01b0381168114620001c357600080fd5b50565b60805160a05160601c60c05160601c60e05160601c613f2562000228600039806104c55280610d61528061109052806115f0525080610556528061158b52508061039252806106bc528061099d52508061131e52806123df5250613f256000f3fe6080604052600436106100ec5760003560e01c80639b552cc21161008a578063ed9f35ce11610059578063ed9f35ce14610274578063f698da2514610294578063f84436bd146102a9578063fbfa77cf146102c9576100f3565b80639b552cc2146101ff578063a2a7d51b14610214578063d08d33d114610234578063ec6cb13f14610254576100f3565b80632479fb6e116100c65780632479fb6e1461016557806343218e19146101925780635624b25b146101bf578063845a101f146101df576100f3565b806313d79a0b146100f857806315337bc01461011a5780632335c76b1461013a576100f3565b366100f357005b600080fd5b34801561010457600080fd5b5061011861011336600461322e565b6102de565b005b34801561012657600080fd5b50610118610135366004613441565b6105c1565b34801561014657600080fd5b5061014f6106ba565b60405161015c91906136ee565b60405180910390f35b34801561017157600080fd5b506101856101803660046134ca565b6106de565b60405161015c91906137f0565b34801561019e57600080fd5b506101b26101ad3660046131a0565b6106fb565b60405161015c919061380d565b3480156101cb57600080fd5b506101b26101da3660046134fd565b610873565b3480156101eb57600080fd5b506101186101fa36600461338e565b6108e9565b34801561020b57600080fd5b5061014f61108e565b34801561022057600080fd5b5061011861022f3660046131ee565b6110b2565b34801561024057600080fd5b5061018561024f3660046134ca565b6110fb565b34801561026057600080fd5b5061011861026f366004613475565b611118565b34801561028057600080fd5b5061011861028f3660046131ee565b6112d7565b3480156102a057600080fd5b5061018561131c565b3480156102b557600080fd5b506101b26102c43660046131a0565b611340565b3480156102d557600080fd5b5061014f611589565b6002600154141561035057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001556040517f02cc250d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906302cc250d906103c79033906004016136ee565b60206040518083038186803b1580156103df57600080fd5b505afa1580156103f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104179190613425565b610456576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613c78565b60405180910390fd5b6104728160005b60200281019061046d9190613d16565b6115ad565b6000806104838989898989896116ea565b6040517f7d10d11f000000000000000000000000000000000000000000000000000000008152919350915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690637d10d11f906104fa90859060040161370f565b600060405180830381600087803b15801561051457600080fd5b505af1158015610528573d6000803e3d6000fd5b5050505061053c8360016003811061045d57fe5b61057c73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001682611851565b61058783600261045d565b60405133907f40338ce1a7c49204f0099533b1e9a7ee0a3d261f84974ab7af36105b8c4e9db490600090a250506001805550505050505050565b60006105cd8383611b2f565b5091505073ffffffffffffffffffffffffffffffffffffffff81163314610620576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613a1b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600284846040516106539291906136c2565b9081526020016040518091039020819055508073ffffffffffffffffffffffffffffffffffffffff167f875b6cb035bbd4ac6500fabc6d1e4ca5bdc58a3e2b424ccb5c24cdbebeb009a984846040516106ad9291906137f9565b60405180910390a2505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b805160208183018101805160028252928201919093012091525481565b606060008373ffffffffffffffffffffffffffffffffffffffff16836040518082805190602001908083835b6020831061076457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610727565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855af49150503d80600081146107c4576040519150601f19603f3d011682016040523d82523d6000602084013e6107c9565b606091505b50809350819250505061086c82826040516020018083805190602001908083835b6020831061082757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107ea565b6001836020036101000a03801982511681845116808217855250505050505090500182151560f81b815260010192505050604051602081830303815290604052611bbd565b5092915050565b606060008260200267ffffffffffffffff8111801561089157600080fd5b506040519080825280601f01601f1916602001820160405280156108bc576020820181803683370190505b50905060005b838110156108df57848101546020808302840101526001016108c2565b5090505b92915050565b6002600154141561095b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001556040517f02cc250d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906302cc250d906109d29033906004016136ee565b60206040518083038186803b1580156109ea57600080fd5b505afa1580156109fe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a229190613425565b610a58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613c78565b6000610a62611bc5565b8051909150610a7382868686611bf2565b60007ff3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee34677582610100015114610aa8576001610aab565b60005b9050610ab5612f90565b60408085015173ffffffffffffffffffffffffffffffffffffffff90811683526101408501517f4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce9081146020850152606080880151909216928401929092526101608501519091149082015260008667ffffffffffffffff81118015610b3a57600080fd5b50604051908082528060200260200182016040528015610b64578160200160208202803683370190505b50610100850151909150610120870135907ff3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee3467751415610c30578460800151811015610bda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613c41565b610be78560600151611c90565b82886000013581518110610bf757fe5b602002602001018181525050610c0c81611c90565b60000382886020013581518110610c1f57fe5b602002602001018181525050610cc0565b8460600151811115610c6e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613b9c565b610c7781611c90565b82886000013581518110610c8757fe5b602002602001018181525050610ca08560800151611c90565b60000382886020013581518110610cb357fe5b6020026020010181815250505b610cc8612f90565b8660400151816000019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508560000151816020019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508560e0015181604001818152505085610140015181606001818152505060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16634817a286878f8f8f8f8b8b8f60a001518b6040518a63ffffffff1660e01b8152600401610dcc99989796959493929190613877565b600060405180830381600087803b158015610de657600080fd5b505af1158015610dfa573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610e4091908101906132ed565b90506000886020015190506000610e6d838c6000013581518110610e6057fe5b6020026020010151611d25565b90506000610e94848d6020013581518110610e8457fe5b6020026020010151600003611d25565b9050600283604051610ea691906136d2565b908152602001604051809103902054600014610eee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613bd3565b7ff3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee3467758a61010001511415610f825789606001518214610f58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613ac0565b8960600151600284604051610f6d91906136d2565b90815260405190819003602001902055610fe5565b89608001518114610fbf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613af7565b8960800151600284604051610fd491906136d2565b908152604051908190036020019020555b8a6040015173ffffffffffffffffffffffffffffffffffffffff167fa07a543ab8a018198e99ca0184c93fe9050a79400a0a723441f84de1d972cc178b600001518c6020015185858f60e001518960405161104596959493929190613820565b60405180910390a260405133907f40338ce1a7c49204f0099533b1e9a7ee0a3d261f84974ab7af36105b8c4e9db490600090a25050600180555050505050505050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b3033146110eb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613b65565b6110f760008383611d96565b5050565b805160208183018101805160008252928201919093012091525481565b60006111248484611b2f565b5091505073ffffffffffffffffffffffffffffffffffffffff811633146111ac57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f475076323a2063616e6e6f74207072657369676e206f72646572000000000000604482015290519081900360640190fd5b8115611206577ff59c009283ff87aa78203fc4d9c2df025ee851130fb69cc3e068941f6b5e2d6f60001c60008585604051808383808284378083019250505092505050908152602001604051809103902081905550611232565b600080858560405180838380828437919091019485525050604051928390036020019092209290925550505b8073ffffffffffffffffffffffffffffffffffffffff167f01bf7c8b0ca55deecbea89d7e58295b7ffbf685fd0d96801034ba8c6ffe1c68d858585604051808060200183151581526020018281038252858582818152602001925080828437600083820152604051601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909201829003965090945050505050a250505050565b303314611310576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613b65565b6110f760028383611d96565b7f000000000000000000000000000000000000000000000000000000000000000081565b606060006343218e1960e01b8484604051602401808373ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b838110156113aa578181015183820152602001611392565b50505050905090810190601f1680156113d75780820380516001836020036101000a031916815260200191505b50604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909816979097178752518151919750309688965090945084935091508083835b602083106114a857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161146b565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461150a576040519150601f19603f3d011682016040523d82523d6000602084013e61150f565b606091505b5090508092505060008260018451038151811061152857fe5b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916600160f81b14905061156b836001855103611e46565b80156115785750506108e3565b61158183611bbd565b505092915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60005b818110156116e557368383838181106115c557fe5b90506020028101906115d79190613dde565b905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001661161d6020830183613184565b73ffffffffffffffffffffffffffffffffffffffff16141561166b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613caf565b61167481611e4a565b6116816020820182613184565b73ffffffffffffffffffffffffffffffffffffffff167fed99827efb37016f2275f98c4bcf71c7551c75d59e9b450f79fa32e60be672c282602001356116c684611ea1565b6040516116d4929190613ce6565b60405180910390a2506001016115b0565b505050565b60608060006116f7611bc5565b90508367ffffffffffffffff8111801561171057600080fd5b5060405190808252806020026020018201604052801561174a57816020015b611737612f90565b81526020019060019003908161172f5790505b5092508367ffffffffffffffff8111801561176457600080fd5b5060405190808252806020026020018201604052801561179e57816020015b61178b612f90565b8152602001906001900390816117835790505b50915060005b8481101561184457368686838181106117b957fe5b90506020028101906117cb9190613e11565b90506117d9838c8c84611bf2565b61183b838a8a84358181106117ea57fe5b905060200201358b8b856020013581811061180157fe5b9050602002013584610120013589878151811061181a57fe5b602002602001015189888151811061182e57fe5b6020026020010151611ecb565b506001016117a4565b5050965096945050505050565b6000815167ffffffffffffffff8111801561186b57600080fd5b506040519080825280602002602001820160405280156118a557816020015b611892612fb7565b81526020019060019003908161188a5790505b5090506000805b8351811015611a935760008482815181106118c357fe5b6020026020010151905073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff1614156119c7577f4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce81606001511415611977576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613b2e565b8051604080830151905173ffffffffffffffffffffffffffffffffffffffff9092169181156108fc0291906000818181858888f193505050501580156119c1573d6000803e3d6000fd5b50611a8a565b7f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc981606001511415611a2657805160408201516020830151611a219273ffffffffffffffffffffffffffffffffffffffff90911691612216565b611a8a565b6000848480600101955081518110611a3a57fe5b602090810291909101810151600081528382015173ffffffffffffffffffffffffffffffffffffffff90811692820192909252604080850151908201523060608201528351909116608090910152505b506001016118ac565b508015611b2957611aa48282611e46565b6040517f0e8e3e8400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851690630e8e3e8490611af690859060040161375d565b600060405180830381600087803b158015611b1057600080fd5b505af1158015611b24573d6000803e3d6000fd5b505050505b50505050565b6000808060388414611ba257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f475076323a20696e76616c696420756964000000000000000000000000000000604482015290519081900360640190fd5b5050823593602084013560601c936034013560e01c92509050565b805160208201fd5b611bcd612fe7565b6040805160388082526060820190925290602082018180368337505050602082015290565b83516000611c02838686856122ee565b9050600080611c1f8484611c1a610140890189613d7b565b6123d6565b91509150611c4282828660a001518b60200151612485909392919063ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff81166040890152611c688482612507565b73ffffffffffffffffffffffffffffffffffffffff1660609098019790975250505050505050565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115611d2157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53616665436173743a20696e74323536206f766572666c6f7700000000000000604482015290519081900360640190fd5b5090565b600080821215611d2157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f53616665436173743a206e6f7420706f73697469766500000000000000000000604482015290519081900360640190fd5b60005b81811015611b2957366000848484818110611db057fe5b9050602002810190611dc29190613d7b565b915091506000611dd28383611b2f565b92505050428163ffffffff1610611e15576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613c0a565b6000878484604051611e289291906136c2565b90815260405190819003602001902055505060019091019050611d99565b9052565b73ffffffffffffffffffffffffffffffffffffffff8135166020820135366000611e776040860186613d7b565b9150915060405181838237600080838387895af1611e99573d6000803e3d6000fd5b505050505050565b60003681611eb26040850185613d7b565b909250905060048110611ec457813592505b5050919050565b8551602087015160a08201514263ffffffff9091161015611f18576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613a52565b6080820151611f279087612539565b6060830151611f369089612539565b1015611f6e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613a89565b6000806000807ff3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee346775866101000151141561206f5785610120015115611fdb57889350611fd48660600151611fce868960e0015161253990919063ffffffff16565b906125c9565b9150611fea565b856060015193508560e0015191505b611ffe8a611ff8868e612539565b9061264a565b925061202a8460028760405161201491906136d2565b90815260405190819003602001902054906126e8565b9050856060015181111561206a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613bd3565b612116565b856101200151156120a35788925061209c8660800151611fce858960e0015161253990919063ffffffff16565b91506120b2565b856080015192508560e0015191505b6120c08b611fce858d612539565b93506120d68360028760405161201491906136d2565b90508560800151811115612116576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613bd3565b61212084836126e8565b93508060028660405161213391906136d2565b9081526020016040518091039020819055508b6040015173ffffffffffffffffffffffffffffffffffffffff167fa07a543ab8a018198e99ca0184c93fe9050a79400a0a723441f84de1d972cc17876000015188602001518787878b6040516121a196959493929190613820565b60405180910390a250506040808b015173ffffffffffffffffffffffffffffffffffffffff9081168852855181166020808a0191909152888301949094526101408601516060988901529a8701518b16865282850151909a169185019190915297830197909752610160015191015250505050565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000080825273ffffffffffffffffffffffffffffffffffffffff84166004830152602482018390529060008060448382895af1612279573d6000803e3d6000fd5b506122838461275c565b611b2957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f475076323a206661696c6564207472616e736665720000000000000000000000604482015290519081900360640190fd5b6000838386358181106122fd57fe5b6020908102929092013573ffffffffffffffffffffffffffffffffffffffff168452508490849087013581811061233057fe5b73ffffffffffffffffffffffffffffffffffffffff602091820293909301358316908501525060408087013590911690830152606080860135908301526080808601359083015263ffffffff60a080870135919091169083015260c0808601359083015260e080860135908301526123ac610100860135612826565b61016087019190915261014086019190915290151561012085015261010090930152509392505050565b600080612403867f000000000000000000000000000000000000000000000000000000000000000061297b565b9150600085600381111561241357fe5b141561242b57612424828585612a05565b905061247c565b600185600381111561243957fe5b141561244a57612424828585612a1a565b600285600381111561245857fe5b141561246957612424828585612a82565b6124798285858960a00151612c20565b90505b94509492505050565b60388451146124f557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f475076323a2075696420627566666572206f766572666c6f7700000000000000604482015290519081900360640190fd5b60388401526034830152602090910152565b604082015160009073ffffffffffffffffffffffffffffffffffffffff166125305750806108e3565b50506040015190565b600082612548575060006108e3565b8282028284828161255557fe5b04146125c257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f536166654d6174683a206d756c206f766572666c6f7700000000000000000000604482015290519081900360640190fd5b9392505050565b600080821161263957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f536166654d6174683a206469766973696f6e2062792030000000000000000000604482015290519081900360640190fd5b81838161264257fe5b049392505050565b60008082116126ba57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f536166654d6174683a206365696c696e67206469766973696f6e206279203000604482015290519081900360640190fd5b8183816126c357fe5b06156126d05760016126d3565b60005b60ff168284816126df57fe5b04019392505050565b6000828201838110156125c257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600061279a565b7f08c379a0000000000000000000000000000000000000000000000000000000006000526020600452806024528160445260646000fd5b3d80156127d95760208114612813576127d47f475076323a206d616c666f726d6564207472616e7366657220726573756c7400601f612763565b612820565b823b61280a5761280a7f475076323a206e6f74206120636f6e74726163740000000000000000000000006014612763565b60019150612820565b3d6000803e600051151591505b50919050565b6000808080806001861661285c577ff3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee3467759450612880565b7f6ed88e868af0a1983e3886d5f3e95a2fafbd6c3450bc229e27342283dc429ccc94505b6002861615159350600886166128b8577f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9925061290c565b600486166128e8577fabee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea0632925061290c565b7f4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce92505b6010861661293c577f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc99150612960565b7f4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce91505b600586901c600381111561297057fe5b905091939590929450565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090910180517fd5a25ba2e97094ad7d83dc28a6572da797d6b3e7fc6663bd93efb789fc17e48982526101a0822091526040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b6000612a12848484612de5565b949350505050565b6000808460405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c01828152602001915050604051602081830303815290604052805190602001209050612a79818585612de5565b95945050505050565b813560601c366000612a978460148188613e68565b604080517f1626ba7e00000000000000000000000000000000000000000000000000000000808252600482018b81526024830193845260448301859052949650929450919273ffffffffffffffffffffffffffffffffffffffff871692631626ba7e928b928892889290606401848480828437600083820152604051601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909201965060209550909350505081840390508186803b158015612b5d57600080fd5b505afa158015612b71573d6000803e3d6000fd5b505050506040513d6020811015612b8757600080fd5b50517fffffffff000000000000000000000000000000000000000000000000000000001614612c1757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f475076323a20696e76616c69642065697031323731207369676e617475726500604482015290519081900360640190fd5b50509392505050565b600060148314612c9157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f475076323a206d616c666f726d6564207072657369676e617475726500000000604482015290519081900360640190fd5b506040805160388082526060828101909352853590921c9160009190602082018180368337019050509050612cc881878486612485565b7ff59c009283ff87aa78203fc4d9c2df025ee851130fb69cc3e068941f6b5e2d6f60001c6000826040518082805190602001908083835b60208310612d3c57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101612cff565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390205414612ddc57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f475076323a206f72646572206e6f74207072657369676e656400000000000000604482015290519081900360640190fd5b50949350505050565b600060418214612e5657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f475076323a206d616c666f726d6564206563647361207369676e617475726500604482015290519081900360640190fd5b604080516000815260208181018084528790528286013560f81c82840181905286356060840181905282880135608085018190529451909493919260019260a0808201937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019281900390910190855afa158015612ed9573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015194505073ffffffffffffffffffffffffffffffffffffffff8416612f8657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f475076323a20696e76616c6964206563647361207369676e6174757265000000604482015290519081900360640190fd5b5050509392505050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6040805160a081019091528060008152600060208201819052604082018190526060820181905260809091015290565b6040518060800160405280612ffa613014565b815260606020820181905260006040830181905291015290565b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810182905261016081019190915290565b60008083601f840112613089578182fd5b50813567ffffffffffffffff8111156130a0578182fd5b60208301915083602080830285010111156130ba57600080fd5b9250929050565b60008083601f8401126130d2578182fd5b50813567ffffffffffffffff8111156130e9578182fd5b6020830191508360208285010111156130ba57600080fd5b600082601f830112613111578081fd5b813567ffffffffffffffff81111561312557fe5b61315660207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613e44565b81815284602083860101111561316a578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215613195578081fd5b81356125c281613ebc565b600080604083850312156131b2578081fd5b82356131bd81613ebc565b9150602083013567ffffffffffffffff8111156131d8578182fd5b6131e485828601613101565b9150509250929050565b60008060208385031215613200578182fd5b823567ffffffffffffffff811115613216578283fd5b61322285828601613078565b90969095509350505050565b60008060008060008060006080888a031215613248578283fd5b873567ffffffffffffffff8082111561325f578485fd5b61326b8b838c01613078565b909950975060208a0135915080821115613283578485fd5b61328f8b838c01613078565b909750955060408a01359150808211156132a7578485fd5b6132b38b838c01613078565b909550935060608a01359150808211156132cb578283fd5b508801606081018a10156132dd578182fd5b8091505092959891949750929550565b600060208083850312156132ff578182fd5b825167ffffffffffffffff80821115613316578384fd5b818501915085601f830112613329578384fd5b81518181111561333557fe5b8381029150613345848301613e44565b8181528481019084860184860187018a101561335f578788fd5b8795505b83861015613381578051835260019590950194918601918601613363565b5098975050505050505050565b6000806000806000606086880312156133a5578081fd5b853567ffffffffffffffff808211156133bc578283fd5b6133c889838a01613078565b909750955060208801359150808211156133e0578283fd5b6133ec89838a01613078565b90955093506040880135915080821115613404578283fd5b5086016101608189031215613417578182fd5b809150509295509295909350565b600060208284031215613436578081fd5b81516125c281613ee1565b60008060208385031215613453578182fd5b823567ffffffffffffffff811115613469578283fd5b613222858286016130c1565b600080600060408486031215613489578081fd5b833567ffffffffffffffff81111561349f578182fd5b6134ab868287016130c1565b90945092505060208401356134bf81613ee1565b809150509250925092565b6000602082840312156134db578081fd5b813567ffffffffffffffff8111156134f1578182fd5b612a1284828501613101565b6000806040838503121561350f578182fd5b50508035926020909101359150565b60008284526020808501945082825b8581101561356857813561354081613ebc565b73ffffffffffffffffffffffffffffffffffffffff168752958201959082019060010161352d565b509495945050505050565b6000815180845260208085019450808401835b8381101561356857815187529582019590820190600101613586565b600082845282826020860137806020848601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011685010190509392505050565b60008151808452613602816020860160208601613e90565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b73ffffffffffffffffffffffffffffffffffffffff8082511683528060208301511660208401525060408101516040830152606081015160608301525050565b73ffffffffffffffffffffffffffffffffffffffff808251168352602082015115156020840152806040830151166040840152506060810151151560608301525050565b63ffffffff169052565b6000828483379101908152919050565b600082516136e4818460208701613e90565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6020808252825182820181905260009190848201906040850190845b818110156137515761373e838551613634565b928401926080929092019160010161372b565b50909695505050505050565b602080825282518282018190526000919060409081850190868401855b828110156137e357815180516004811061379057fe5b85528087015173ffffffffffffffffffffffffffffffffffffffff908116888701528682015187870152606080830151821690870152608091820151169085015260a0909301929085019060010161377a565b5091979650505050505050565b90815260200190565b600060208252612a126020830184866135a2565b6000602082526125c260208301846135ea565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015283608083015260c060a083015261386b60c08301846135ea565b98975050505050505050565b60006101a0820160028c1061388857fe5b8b835260206101a081850152818b83526101c0850190506101c0828d0286010192508c845b8d8110156139b6577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe408786030183527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618f36030182351261390c578586fd5b8e823501803586528481013585870152604081013560408701526060810135606087015260808101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1823603018112613964578788fd5b8101803567ffffffffffffffff81111561397c578889fd5b80360383131561398a578889fd5b60a060808901526139a160a08901828985016135a2565b975050509284019250908301906001016138ad565b5050505082810360408401526139cd81898b61351e565b90506139dc6060840188613674565b82810360e08401526139ee8187613573565b9150506139ff6101008301856136b8565b613a0d610120830184613634565b9a9950505050505050505050565b6020808252601f908201527f475076323a2063616c6c657220646f6573206e6f74206f776e206f7264657200604082015260600190565b60208082526013908201527f475076323a206f72646572206578706972656400000000000000000000000000604082015260600190565b6020808252601f908201527f475076323a206c696d6974207072696365206e6f742072657370656374656400604082015260600190565b6020808252601f908201527f475076323a2073656c6c20616d6f756e74206e6f742072657370656374656400604082015260600190565b6020808252601e908201527f475076323a2062757920616d6f756e74206e6f74207265737065637465640000604082015260600190565b6020808252601e908201527f475076323a20756e737570706f7274656420696e7465726e616c204554480000604082015260600190565b60208082526018908201527f475076323a206e6f7420616e20696e746572616374696f6e0000000000000000604082015260600190565b60208082526014908201527f475076323a206c696d697420746f6f2068696768000000000000000000000000604082015260600190565b60208082526012908201527f475076323a206f726465722066696c6c65640000000000000000000000000000604082015260600190565b60208082526017908201527f475076323a206f72646572207374696c6c2076616c6964000000000000000000604082015260600190565b60208082526013908201527f475076323a206c696d697420746f6f206c6f7700000000000000000000000000604082015260600190565b60208082526012908201527f475076323a206e6f74206120736f6c7665720000000000000000000000000000604082015260600190565b6020808252601b908201527f475076323a20666f7262696464656e20696e746572616374696f6e0000000000604082015260600190565b9182527fffffffff0000000000000000000000000000000000000000000000000000000016602082015260400190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613d4a578283fd5b83018035915067ffffffffffffffff821115613d64578283fd5b60209081019250810236038213156130ba57600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613daf578283fd5b83018035915067ffffffffffffffff821115613dc9578283fd5b6020019150368190038213156130ba57600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18336030181126136e4578182fd5b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea18336030181126136e4578182fd5b60405181810167ffffffffffffffff81118282101715613e6057fe5b604052919050565b60008085851115613e77578182fd5b83861115613e83578182fd5b5050820193919092039150565b60005b83811015613eab578181015183820152602001613e93565b83811115611b295750506000910152565b73ffffffffffffffffffffffffffffffffffffffff81168114613ede57600080fd5b50565b8015158114613ede57600080fdfea2646970667358221220de5e493c48a3b42da03a5db89085177b8d8ccec6e9bf6e8e48b3809343624c8f64736f6c6343000706003360c060405234801561001057600080fd5b5060405161129e38038061129e83398101604081905261002f9161004b565b33606090811b6080521b6001600160601b03191660a052610079565b60006020828403121561005c578081fd5b81516001600160a01b0381168114610072578182fd5b9392505050565b60805160601c60a05160601c6111ee6100b060003980610130528061020152806102bd5250806093528061024c52506111ee6000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80634817a2861461003b5780637d10d11f14610064575b600080fd5b61004e610049366004610cd9565b610079565b60405161005b9190610eb3565b60405180910390f35b610077610072366004610c69565b610234565b005b60603373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146100f3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ea906110e5565b60405180910390fd5b6040517f945bcec900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063945bcec990610171908c908c908c908c908c908c908c90600401610f59565b600060405180830381600087803b15801561018b57600080fd5b505af115801561019f573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526101e59190810190610bd9565b905061022873ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001683336102e9565b98975050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146102a3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ea906110e5565b6102e573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016838333610551565b5050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61030e6040840160208501610bb6565b73ffffffffffffffffffffffffffffffffffffffff16141561035c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ea9061111c565b7f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9826060013514156103d0576103cb6103986020840184610bb6565b82604085018035906103ad9060208801610bb6565b73ffffffffffffffffffffffffffffffffffffffff16929190610816565b61054c565b604080516001808252818301909252600091816020015b6103ef6109cb565b8152602001906001900390816103e757905050905060008160008151811061041357fe5b602002602001015190507fabee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea063284606001351461044f576002610452565b60035b8190600381111561045f57fe5b9081600381111561046c57fe5b90525061047f6040850160208601610bb6565b73ffffffffffffffffffffffffffffffffffffffff16602080830191909152604080860135908301526104b490850185610bb6565b73ffffffffffffffffffffffffffffffffffffffff908116606083015283811660808301526040517f0e8e3e8400000000000000000000000000000000000000000000000000000000815290861690630e8e3e8490610517908590600401610ec6565b600060405180830381600087803b15801561053157600080fd5b505af1158015610545573d6000803e3d6000fd5b5050505050505b505050565b60008267ffffffffffffffff8111801561056a57600080fd5b506040519080825280602002602001820160405280156105a457816020015b6105916109cb565b8152602001906001900390816105895790505b5090506000805b8481101561077857368686838181106105c057fe5b60800291909101915073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee90506105f06040830160208401610bb6565b73ffffffffffffffffffffffffffffffffffffffff16141561063e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ea9061111c565b7f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9816060013514156106945761068f61067a6020830183610bb6565b86604084018035906103ad9060208701610bb6565b61076f565b60008484806001019550815181106106a857fe5b602002602001015190507fabee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea06328260600135146106e45760016106e7565b60035b819060038111156106f457fe5b9081600381111561070157fe5b9052506107146040830160208401610bb6565b73ffffffffffffffffffffffffffffffffffffffff166020808301919091526040808401359083015261074990830183610bb6565b73ffffffffffffffffffffffffffffffffffffffff908116606083015286166080909101525b506001016105ab565b50801561080e5761078982826108fd565b6040517f0e8e3e8400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871690630e8e3e84906107db908590600401610ec6565b600060405180830381600087803b1580156107f557600080fd5b505af1158015610809573d6000803e3d6000fd5b505050505b505050505050565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000080825273ffffffffffffffffffffffffffffffffffffffff8581166004840152841660248301526044820183905290600080606483828a5af1610881573d6000803e3d6000fd5b5061088b85610901565b6108f657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f475076323a206661696c6564207472616e7366657246726f6d00000000000000604482015290519081900360640190fd5b5050505050565b9052565b600061093f565b7f08c379a0000000000000000000000000000000000000000000000000000000006000526020600452806024528160445260646000fd5b3d801561097e57602081146109b8576109797f475076323a206d616c666f726d6564207472616e7366657220726573756c7400601f610908565b6109c5565b823b6109af576109af7f475076323a206e6f74206120636f6e74726163740000000000000000000000006014610908565b600191506109c5565b3d6000803e600051151591505b50919050565b6040805160a081019091528060008152600060208201819052604082018190526060820181905260809091015290565b600082601f830112610a0b578081fd5b81356020610a20610a1b83611175565b611151565b8281528181019085830183850287018401881015610a3c578586fd5b855b85811015610a63578135610a5181611193565b84529284019290840190600101610a3e565b5090979650505050505050565b600082601f830112610a80578081fd5b81356020610a90610a1b83611175565b8281528181019085830183850287018401881015610aac578586fd5b855b85811015610a6357813584529284019290840190600101610aae565b60008083601f840112610adb578182fd5b50813567ffffffffffffffff811115610af2578182fd5b6020830191508360208083028501011115610b0c57600080fd5b9250929050565b80358015158114610b2357600080fd5b919050565b6000608082840312156109c5578081fd5b600060808284031215610b4a578081fd5b6040516080810181811067ffffffffffffffff82111715610b6757fe5b6040529050808235610b7881611193565b8152610b8660208401610b13565b60208201526040830135610b9981611193565b6040820152610baa60608401610b13565b60608201525092915050565b600060208284031215610bc7578081fd5b8135610bd281611193565b9392505050565b60006020808385031215610beb578182fd5b825167ffffffffffffffff811115610c01578283fd5b8301601f81018513610c11578283fd5b8051610c1f610a1b82611175565b8181528381019083850185840285018601891015610c3b578687fd5b8694505b83851015610c5d578051835260019490940193918501918501610c3f565b50979650505050505050565b60008060208385031215610c7b578081fd5b823567ffffffffffffffff80821115610c92578283fd5b818501915085601f830112610ca5578283fd5b813581811115610cb3578384fd5b866020608083028501011115610cc7578384fd5b60209290920196919550909350505050565b6000806000806000806000806101a0898b031215610cf5578384fd5b883560028110610d03578485fd5b9750602089013567ffffffffffffffff80821115610d1f578586fd5b610d2b8c838d01610aca565b909950975060408b0135915080821115610d43578586fd5b610d4f8c838d016109fb565b9650610d5e8c60608d01610b39565b955060e08b0135915080821115610d73578485fd5b50610d808b828c01610a70565b9350506101008901359150610d998a6101208b01610b28565b90509295985092959890939650565b6000815180845260208085019450808401835b83811015610ded57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101610dbb565b509495945050505050565b6000815180845260208085019450808401835b83811015610ded57815187529582019590820190600101610e0b565b600082845282826020860137806020848601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011685010190509392505050565b73ffffffffffffffffffffffffffffffffffffffff808251168352602082015115156020840152806040830151166040840152506060810151151560608301525050565b600060208252610bd26020830184610df8565b602080825282518282018190526000919060409081850190868401855b82811015610f4c578151805160048110610ef957fe5b85528087015173ffffffffffffffffffffffffffffffffffffffff908116888701528682015187870152606080830151821690870152608091820151169085015260a09093019290850190600101610ee3565b5091979650505050505050565b600061012080830160028b10610f6b57fe5b8a8452602080850192909252889052610140808401918981028501909101908a845b8b811015611098577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec087850301855281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618e3603018112610fed578687fd5b8d01803585528381013584860152604080820135908601526060808201359086015260a0608080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe101811261104757898afd5b8301803567ffffffffffffffff81111561105f578a8bfd5b80360385131561106d578a8bfd5b83838a0152611081848a01828a8501610e27565b998801999850505093850193505050600101610f8d565b50505083810360408501526110ad8189610da8565b9150506110bd6060840187610e6f565b82810360e08401526110cf8186610df8565b9150508261010083015298975050505050505050565b60208082526011908201527f475076323a206e6f742063726561746f72000000000000000000000000000000604082015260600190565b6020808252818101527f475076323a2063616e6e6f74207472616e73666572206e617469766520455448604082015260600190565b60405181810167ffffffffffffffff8111828210171561116d57fe5b604052919050565b600067ffffffffffffffff82111561118957fe5b5060209081020190565b73ffffffffffffffffffffffffffffffffffffffff811681146111b557600080fd5b5056fea2646970667358221220364a6941bea69620b7dc3a957d0ab4cbf3bfc459c7ad3924d220620aca9202fc64736f6c634300070600330000000000000000000000002c4c28ddbdac9c5e7055b4c863b72ea0149d8afe000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
Deployed Bytecode
0x6080604052600436106100ec5760003560e01c80639b552cc21161008a578063ed9f35ce11610059578063ed9f35ce14610274578063f698da2514610294578063f84436bd146102a9578063fbfa77cf146102c9576100f3565b80639b552cc2146101ff578063a2a7d51b14610214578063d08d33d114610234578063ec6cb13f14610254576100f3565b80632479fb6e116100c65780632479fb6e1461016557806343218e19146101925780635624b25b146101bf578063845a101f146101df576100f3565b806313d79a0b146100f857806315337bc01461011a5780632335c76b1461013a576100f3565b366100f357005b600080fd5b34801561010457600080fd5b5061011861011336600461322e565b6102de565b005b34801561012657600080fd5b50610118610135366004613441565b6105c1565b34801561014657600080fd5b5061014f6106ba565b60405161015c91906136ee565b60405180910390f35b34801561017157600080fd5b506101856101803660046134ca565b6106de565b60405161015c91906137f0565b34801561019e57600080fd5b506101b26101ad3660046131a0565b6106fb565b60405161015c919061380d565b3480156101cb57600080fd5b506101b26101da3660046134fd565b610873565b3480156101eb57600080fd5b506101186101fa36600461338e565b6108e9565b34801561020b57600080fd5b5061014f61108e565b34801561022057600080fd5b5061011861022f3660046131ee565b6110b2565b34801561024057600080fd5b5061018561024f3660046134ca565b6110fb565b34801561026057600080fd5b5061011861026f366004613475565b611118565b34801561028057600080fd5b5061011861028f3660046131ee565b6112d7565b3480156102a057600080fd5b5061018561131c565b3480156102b557600080fd5b506101b26102c43660046131a0565b611340565b3480156102d557600080fd5b5061014f611589565b6002600154141561035057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001556040517f02cc250d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002c4c28ddbdac9c5e7055b4c863b72ea0149d8afe16906302cc250d906103c79033906004016136ee565b60206040518083038186803b1580156103df57600080fd5b505afa1580156103f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104179190613425565b610456576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613c78565b60405180910390fd5b6104728160005b60200281019061046d9190613d16565b6115ad565b6000806104838989898989896116ea565b6040517f7d10d11f000000000000000000000000000000000000000000000000000000008152919350915073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c92e8bdf79f0507f65a392b0ab4667716bfe01101690637d10d11f906104fa90859060040161370f565b600060405180830381600087803b15801561051457600080fd5b505af1158015610528573d6000803e3d6000fd5b5050505061053c8360016003811061045d57fe5b61057c73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c81682611851565b61058783600261045d565b60405133907f40338ce1a7c49204f0099533b1e9a7ee0a3d261f84974ab7af36105b8c4e9db490600090a250506001805550505050505050565b60006105cd8383611b2f565b5091505073ffffffffffffffffffffffffffffffffffffffff81163314610620576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613a1b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600284846040516106539291906136c2565b9081526020016040518091039020819055508073ffffffffffffffffffffffffffffffffffffffff167f875b6cb035bbd4ac6500fabc6d1e4ca5bdc58a3e2b424ccb5c24cdbebeb009a984846040516106ad9291906137f9565b60405180910390a2505050565b7f0000000000000000000000002c4c28ddbdac9c5e7055b4c863b72ea0149d8afe81565b805160208183018101805160028252928201919093012091525481565b606060008373ffffffffffffffffffffffffffffffffffffffff16836040518082805190602001908083835b6020831061076457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610727565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855af49150503d80600081146107c4576040519150601f19603f3d011682016040523d82523d6000602084013e6107c9565b606091505b50809350819250505061086c82826040516020018083805190602001908083835b6020831061082757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107ea565b6001836020036101000a03801982511681845116808217855250505050505090500182151560f81b815260010192505050604051602081830303815290604052611bbd565b5092915050565b606060008260200267ffffffffffffffff8111801561089157600080fd5b506040519080825280601f01601f1916602001820160405280156108bc576020820181803683370190505b50905060005b838110156108df57848101546020808302840101526001016108c2565b5090505b92915050565b6002600154141561095b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026001556040517f02cc250d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002c4c28ddbdac9c5e7055b4c863b72ea0149d8afe16906302cc250d906109d29033906004016136ee565b60206040518083038186803b1580156109ea57600080fd5b505afa1580156109fe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a229190613425565b610a58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613c78565b6000610a62611bc5565b8051909150610a7382868686611bf2565b60007ff3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee34677582610100015114610aa8576001610aab565b60005b9050610ab5612f90565b60408085015173ffffffffffffffffffffffffffffffffffffffff90811683526101408501517f4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce9081146020850152606080880151909216928401929092526101608501519091149082015260008667ffffffffffffffff81118015610b3a57600080fd5b50604051908082528060200260200182016040528015610b64578160200160208202803683370190505b50610100850151909150610120870135907ff3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee3467751415610c30578460800151811015610bda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613c41565b610be78560600151611c90565b82886000013581518110610bf757fe5b602002602001018181525050610c0c81611c90565b60000382886020013581518110610c1f57fe5b602002602001018181525050610cc0565b8460600151811115610c6e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613b9c565b610c7781611c90565b82886000013581518110610c8757fe5b602002602001018181525050610ca08560800151611c90565b60000382886020013581518110610cb357fe5b6020026020010181815250505b610cc8612f90565b8660400151816000019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508560000151816020019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508560e0015181604001818152505085610140015181606001818152505060007f000000000000000000000000c92e8bdf79f0507f65a392b0ab4667716bfe011073ffffffffffffffffffffffffffffffffffffffff16634817a286878f8f8f8f8b8b8f60a001518b6040518a63ffffffff1660e01b8152600401610dcc99989796959493929190613877565b600060405180830381600087803b158015610de657600080fd5b505af1158015610dfa573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610e4091908101906132ed565b90506000886020015190506000610e6d838c6000013581518110610e6057fe5b6020026020010151611d25565b90506000610e94848d6020013581518110610e8457fe5b6020026020010151600003611d25565b9050600283604051610ea691906136d2565b908152602001604051809103902054600014610eee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613bd3565b7ff3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee3467758a61010001511415610f825789606001518214610f58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613ac0565b8960600151600284604051610f6d91906136d2565b90815260405190819003602001902055610fe5565b89608001518114610fbf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613af7565b8960800151600284604051610fd491906136d2565b908152604051908190036020019020555b8a6040015173ffffffffffffffffffffffffffffffffffffffff167fa07a543ab8a018198e99ca0184c93fe9050a79400a0a723441f84de1d972cc178b600001518c6020015185858f60e001518960405161104596959493929190613820565b60405180910390a260405133907f40338ce1a7c49204f0099533b1e9a7ee0a3d261f84974ab7af36105b8c4e9db490600090a25050600180555050505050505050505050505050565b7f000000000000000000000000c92e8bdf79f0507f65a392b0ab4667716bfe011081565b3033146110eb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613b65565b6110f760008383611d96565b5050565b805160208183018101805160008252928201919093012091525481565b60006111248484611b2f565b5091505073ffffffffffffffffffffffffffffffffffffffff811633146111ac57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f475076323a2063616e6e6f74207072657369676e206f72646572000000000000604482015290519081900360640190fd5b8115611206577ff59c009283ff87aa78203fc4d9c2df025ee851130fb69cc3e068941f6b5e2d6f60001c60008585604051808383808284378083019250505092505050908152602001604051809103902081905550611232565b600080858560405180838380828437919091019485525050604051928390036020019092209290925550505b8073ffffffffffffffffffffffffffffffffffffffff167f01bf7c8b0ca55deecbea89d7e58295b7ffbf685fd0d96801034ba8c6ffe1c68d858585604051808060200183151581526020018281038252858582818152602001925080828437600083820152604051601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909201829003965090945050505050a250505050565b303314611310576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613b65565b6110f760028383611d96565b7f69d78e7a7cafcaf924483f99f65e8f4e303a99a446db7ab319f9d40e940bced281565b606060006343218e1960e01b8484604051602401808373ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b838110156113aa578181015183820152602001611392565b50505050905090810190601f1680156113d75780820380516001836020036101000a031916815260200191505b50604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909816979097178752518151919750309688965090945084935091508083835b602083106114a857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161146b565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461150a576040519150601f19603f3d011682016040523d82523d6000602084013e61150f565b606091505b5090508092505060008260018451038151811061152857fe5b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916600160f81b14905061156b836001855103611e46565b80156115785750506108e3565b61158183611bbd565b505092915050565b7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c881565b60005b818110156116e557368383838181106115c557fe5b90506020028101906115d79190613dde565b905073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c92e8bdf79f0507f65a392b0ab4667716bfe01101661161d6020830183613184565b73ffffffffffffffffffffffffffffffffffffffff16141561166b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613caf565b61167481611e4a565b6116816020820182613184565b73ffffffffffffffffffffffffffffffffffffffff167fed99827efb37016f2275f98c4bcf71c7551c75d59e9b450f79fa32e60be672c282602001356116c684611ea1565b6040516116d4929190613ce6565b60405180910390a2506001016115b0565b505050565b60608060006116f7611bc5565b90508367ffffffffffffffff8111801561171057600080fd5b5060405190808252806020026020018201604052801561174a57816020015b611737612f90565b81526020019060019003908161172f5790505b5092508367ffffffffffffffff8111801561176457600080fd5b5060405190808252806020026020018201604052801561179e57816020015b61178b612f90565b8152602001906001900390816117835790505b50915060005b8481101561184457368686838181106117b957fe5b90506020028101906117cb9190613e11565b90506117d9838c8c84611bf2565b61183b838a8a84358181106117ea57fe5b905060200201358b8b856020013581811061180157fe5b9050602002013584610120013589878151811061181a57fe5b602002602001015189888151811061182e57fe5b6020026020010151611ecb565b506001016117a4565b5050965096945050505050565b6000815167ffffffffffffffff8111801561186b57600080fd5b506040519080825280602002602001820160405280156118a557816020015b611892612fb7565b81526020019060019003908161188a5790505b5090506000805b8351811015611a935760008482815181106118c357fe5b6020026020010151905073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff16816020015173ffffffffffffffffffffffffffffffffffffffff1614156119c7577f4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce81606001511415611977576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613b2e565b8051604080830151905173ffffffffffffffffffffffffffffffffffffffff9092169181156108fc0291906000818181858888f193505050501580156119c1573d6000803e3d6000fd5b50611a8a565b7f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc981606001511415611a2657805160408201516020830151611a219273ffffffffffffffffffffffffffffffffffffffff90911691612216565b611a8a565b6000848480600101955081518110611a3a57fe5b602090810291909101810151600081528382015173ffffffffffffffffffffffffffffffffffffffff90811692820192909252604080850151908201523060608201528351909116608090910152505b506001016118ac565b508015611b2957611aa48282611e46565b6040517f0e8e3e8400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851690630e8e3e8490611af690859060040161375d565b600060405180830381600087803b158015611b1057600080fd5b505af1158015611b24573d6000803e3d6000fd5b505050505b50505050565b6000808060388414611ba257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f475076323a20696e76616c696420756964000000000000000000000000000000604482015290519081900360640190fd5b5050823593602084013560601c936034013560e01c92509050565b805160208201fd5b611bcd612fe7565b6040805160388082526060820190925290602082018180368337505050602082015290565b83516000611c02838686856122ee565b9050600080611c1f8484611c1a610140890189613d7b565b6123d6565b91509150611c4282828660a001518b60200151612485909392919063ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff81166040890152611c688482612507565b73ffffffffffffffffffffffffffffffffffffffff1660609098019790975250505050505050565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115611d2157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53616665436173743a20696e74323536206f766572666c6f7700000000000000604482015290519081900360640190fd5b5090565b600080821215611d2157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f53616665436173743a206e6f7420706f73697469766500000000000000000000604482015290519081900360640190fd5b60005b81811015611b2957366000848484818110611db057fe5b9050602002810190611dc29190613d7b565b915091506000611dd28383611b2f565b92505050428163ffffffff1610611e15576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613c0a565b6000878484604051611e289291906136c2565b90815260405190819003602001902055505060019091019050611d99565b9052565b73ffffffffffffffffffffffffffffffffffffffff8135166020820135366000611e776040860186613d7b565b9150915060405181838237600080838387895af1611e99573d6000803e3d6000fd5b505050505050565b60003681611eb26040850185613d7b565b909250905060048110611ec457813592505b5050919050565b8551602087015160a08201514263ffffffff9091161015611f18576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613a52565b6080820151611f279087612539565b6060830151611f369089612539565b1015611f6e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613a89565b6000806000807ff3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee346775866101000151141561206f5785610120015115611fdb57889350611fd48660600151611fce868960e0015161253990919063ffffffff16565b906125c9565b9150611fea565b856060015193508560e0015191505b611ffe8a611ff8868e612539565b9061264a565b925061202a8460028760405161201491906136d2565b90815260405190819003602001902054906126e8565b9050856060015181111561206a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613bd3565b612116565b856101200151156120a35788925061209c8660800151611fce858960e0015161253990919063ffffffff16565b91506120b2565b856080015192508560e0015191505b6120c08b611fce858d612539565b93506120d68360028760405161201491906136d2565b90508560800151811115612116576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044d90613bd3565b61212084836126e8565b93508060028660405161213391906136d2565b9081526020016040518091039020819055508b6040015173ffffffffffffffffffffffffffffffffffffffff167fa07a543ab8a018198e99ca0184c93fe9050a79400a0a723441f84de1d972cc17876000015188602001518787878b6040516121a196959493929190613820565b60405180910390a250506040808b015173ffffffffffffffffffffffffffffffffffffffff9081168852855181166020808a0191909152888301949094526101408601516060988901529a8701518b16865282850151909a169185019190915297830197909752610160015191015250505050565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000080825273ffffffffffffffffffffffffffffffffffffffff84166004830152602482018390529060008060448382895af1612279573d6000803e3d6000fd5b506122838461275c565b611b2957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f475076323a206661696c6564207472616e736665720000000000000000000000604482015290519081900360640190fd5b6000838386358181106122fd57fe5b6020908102929092013573ffffffffffffffffffffffffffffffffffffffff168452508490849087013581811061233057fe5b73ffffffffffffffffffffffffffffffffffffffff602091820293909301358316908501525060408087013590911690830152606080860135908301526080808601359083015263ffffffff60a080870135919091169083015260c0808601359083015260e080860135908301526123ac610100860135612826565b61016087019190915261014086019190915290151561012085015261010090930152509392505050565b600080612403867f69d78e7a7cafcaf924483f99f65e8f4e303a99a446db7ab319f9d40e940bced261297b565b9150600085600381111561241357fe5b141561242b57612424828585612a05565b905061247c565b600185600381111561243957fe5b141561244a57612424828585612a1a565b600285600381111561245857fe5b141561246957612424828585612a82565b6124798285858960a00151612c20565b90505b94509492505050565b60388451146124f557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f475076323a2075696420627566666572206f766572666c6f7700000000000000604482015290519081900360640190fd5b60388401526034830152602090910152565b604082015160009073ffffffffffffffffffffffffffffffffffffffff166125305750806108e3565b50506040015190565b600082612548575060006108e3565b8282028284828161255557fe5b04146125c257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f536166654d6174683a206d756c206f766572666c6f7700000000000000000000604482015290519081900360640190fd5b9392505050565b600080821161263957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f536166654d6174683a206469766973696f6e2062792030000000000000000000604482015290519081900360640190fd5b81838161264257fe5b049392505050565b60008082116126ba57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f536166654d6174683a206365696c696e67206469766973696f6e206279203000604482015290519081900360640190fd5b8183816126c357fe5b06156126d05760016126d3565b60005b60ff168284816126df57fe5b04019392505050565b6000828201838110156125c257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600061279a565b7f08c379a0000000000000000000000000000000000000000000000000000000006000526020600452806024528160445260646000fd5b3d80156127d95760208114612813576127d47f475076323a206d616c666f726d6564207472616e7366657220726573756c7400601f612763565b612820565b823b61280a5761280a7f475076323a206e6f74206120636f6e74726163740000000000000000000000006014612763565b60019150612820565b3d6000803e600051151591505b50919050565b6000808080806001861661285c577ff3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee3467759450612880565b7f6ed88e868af0a1983e3886d5f3e95a2fafbd6c3450bc229e27342283dc429ccc94505b6002861615159350600886166128b8577f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9925061290c565b600486166128e8577fabee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea0632925061290c565b7f4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce92505b6010861661293c577f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc99150612960565b7f4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce91505b600586901c600381111561297057fe5b905091939590929450565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090910180517fd5a25ba2e97094ad7d83dc28a6572da797d6b3e7fc6663bd93efb789fc17e48982526101a0822091526040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b6000612a12848484612de5565b949350505050565b6000808460405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c01828152602001915050604051602081830303815290604052805190602001209050612a79818585612de5565b95945050505050565b813560601c366000612a978460148188613e68565b604080517f1626ba7e00000000000000000000000000000000000000000000000000000000808252600482018b81526024830193845260448301859052949650929450919273ffffffffffffffffffffffffffffffffffffffff871692631626ba7e928b928892889290606401848480828437600083820152604051601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909201965060209550909350505081840390508186803b158015612b5d57600080fd5b505afa158015612b71573d6000803e3d6000fd5b505050506040513d6020811015612b8757600080fd5b50517fffffffff000000000000000000000000000000000000000000000000000000001614612c1757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f475076323a20696e76616c69642065697031323731207369676e617475726500604482015290519081900360640190fd5b50509392505050565b600060148314612c9157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f475076323a206d616c666f726d6564207072657369676e617475726500000000604482015290519081900360640190fd5b506040805160388082526060828101909352853590921c9160009190602082018180368337019050509050612cc881878486612485565b7ff59c009283ff87aa78203fc4d9c2df025ee851130fb69cc3e068941f6b5e2d6f60001c6000826040518082805190602001908083835b60208310612d3c57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101612cff565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390205414612ddc57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f475076323a206f72646572206e6f74207072657369676e656400000000000000604482015290519081900360640190fd5b50949350505050565b600060418214612e5657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f475076323a206d616c666f726d6564206563647361207369676e617475726500604482015290519081900360640190fd5b604080516000815260208181018084528790528286013560f81c82840181905286356060840181905282880135608085018190529451909493919260019260a0808201937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081019281900390910190855afa158015612ed9573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015194505073ffffffffffffffffffffffffffffffffffffffff8416612f8657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f475076323a20696e76616c6964206563647361207369676e6174757265000000604482015290519081900360640190fd5b5050509392505050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6040805160a081019091528060008152600060208201819052604082018190526060820181905260809091015290565b6040518060800160405280612ffa613014565b815260606020820181905260006040830181905291015290565b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810182905261016081019190915290565b60008083601f840112613089578182fd5b50813567ffffffffffffffff8111156130a0578182fd5b60208301915083602080830285010111156130ba57600080fd5b9250929050565b60008083601f8401126130d2578182fd5b50813567ffffffffffffffff8111156130e9578182fd5b6020830191508360208285010111156130ba57600080fd5b600082601f830112613111578081fd5b813567ffffffffffffffff81111561312557fe5b61315660207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613e44565b81815284602083860101111561316a578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215613195578081fd5b81356125c281613ebc565b600080604083850312156131b2578081fd5b82356131bd81613ebc565b9150602083013567ffffffffffffffff8111156131d8578182fd5b6131e485828601613101565b9150509250929050565b60008060208385031215613200578182fd5b823567ffffffffffffffff811115613216578283fd5b61322285828601613078565b90969095509350505050565b60008060008060008060006080888a031215613248578283fd5b873567ffffffffffffffff8082111561325f578485fd5b61326b8b838c01613078565b909950975060208a0135915080821115613283578485fd5b61328f8b838c01613078565b909750955060408a01359150808211156132a7578485fd5b6132b38b838c01613078565b909550935060608a01359150808211156132cb578283fd5b508801606081018a10156132dd578182fd5b8091505092959891949750929550565b600060208083850312156132ff578182fd5b825167ffffffffffffffff80821115613316578384fd5b818501915085601f830112613329578384fd5b81518181111561333557fe5b8381029150613345848301613e44565b8181528481019084860184860187018a101561335f578788fd5b8795505b83861015613381578051835260019590950194918601918601613363565b5098975050505050505050565b6000806000806000606086880312156133a5578081fd5b853567ffffffffffffffff808211156133bc578283fd5b6133c889838a01613078565b909750955060208801359150808211156133e0578283fd5b6133ec89838a01613078565b90955093506040880135915080821115613404578283fd5b5086016101608189031215613417578182fd5b809150509295509295909350565b600060208284031215613436578081fd5b81516125c281613ee1565b60008060208385031215613453578182fd5b823567ffffffffffffffff811115613469578283fd5b613222858286016130c1565b600080600060408486031215613489578081fd5b833567ffffffffffffffff81111561349f578182fd5b6134ab868287016130c1565b90945092505060208401356134bf81613ee1565b809150509250925092565b6000602082840312156134db578081fd5b813567ffffffffffffffff8111156134f1578182fd5b612a1284828501613101565b6000806040838503121561350f578182fd5b50508035926020909101359150565b60008284526020808501945082825b8581101561356857813561354081613ebc565b73ffffffffffffffffffffffffffffffffffffffff168752958201959082019060010161352d565b509495945050505050565b6000815180845260208085019450808401835b8381101561356857815187529582019590820190600101613586565b600082845282826020860137806020848601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011685010190509392505050565b60008151808452613602816020860160208601613e90565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b73ffffffffffffffffffffffffffffffffffffffff8082511683528060208301511660208401525060408101516040830152606081015160608301525050565b73ffffffffffffffffffffffffffffffffffffffff808251168352602082015115156020840152806040830151166040840152506060810151151560608301525050565b63ffffffff169052565b6000828483379101908152919050565b600082516136e4818460208701613e90565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6020808252825182820181905260009190848201906040850190845b818110156137515761373e838551613634565b928401926080929092019160010161372b565b50909695505050505050565b602080825282518282018190526000919060409081850190868401855b828110156137e357815180516004811061379057fe5b85528087015173ffffffffffffffffffffffffffffffffffffffff908116888701528682015187870152606080830151821690870152608091820151169085015260a0909301929085019060010161377a565b5091979650505050505050565b90815260200190565b600060208252612a126020830184866135a2565b6000602082526125c260208301846135ea565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015283608083015260c060a083015261386b60c08301846135ea565b98975050505050505050565b60006101a0820160028c1061388857fe5b8b835260206101a081850152818b83526101c0850190506101c0828d0286010192508c845b8d8110156139b6577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe408786030183527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618f36030182351261390c578586fd5b8e823501803586528481013585870152604081013560408701526060810135606087015260808101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1823603018112613964578788fd5b8101803567ffffffffffffffff81111561397c578889fd5b80360383131561398a578889fd5b60a060808901526139a160a08901828985016135a2565b975050509284019250908301906001016138ad565b5050505082810360408401526139cd81898b61351e565b90506139dc6060840188613674565b82810360e08401526139ee8187613573565b9150506139ff6101008301856136b8565b613a0d610120830184613634565b9a9950505050505050505050565b6020808252601f908201527f475076323a2063616c6c657220646f6573206e6f74206f776e206f7264657200604082015260600190565b60208082526013908201527f475076323a206f72646572206578706972656400000000000000000000000000604082015260600190565b6020808252601f908201527f475076323a206c696d6974207072696365206e6f742072657370656374656400604082015260600190565b6020808252601f908201527f475076323a2073656c6c20616d6f756e74206e6f742072657370656374656400604082015260600190565b6020808252601e908201527f475076323a2062757920616d6f756e74206e6f74207265737065637465640000604082015260600190565b6020808252601e908201527f475076323a20756e737570706f7274656420696e7465726e616c204554480000604082015260600190565b60208082526018908201527f475076323a206e6f7420616e20696e746572616374696f6e0000000000000000604082015260600190565b60208082526014908201527f475076323a206c696d697420746f6f2068696768000000000000000000000000604082015260600190565b60208082526012908201527f475076323a206f726465722066696c6c65640000000000000000000000000000604082015260600190565b60208082526017908201527f475076323a206f72646572207374696c6c2076616c6964000000000000000000604082015260600190565b60208082526013908201527f475076323a206c696d697420746f6f206c6f7700000000000000000000000000604082015260600190565b60208082526012908201527f475076323a206e6f74206120736f6c7665720000000000000000000000000000604082015260600190565b6020808252601b908201527f475076323a20666f7262696464656e20696e746572616374696f6e0000000000604082015260600190565b9182527fffffffff0000000000000000000000000000000000000000000000000000000016602082015260400190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613d4a578283fd5b83018035915067ffffffffffffffff821115613d64578283fd5b60209081019250810236038213156130ba57600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613daf578283fd5b83018035915067ffffffffffffffff821115613dc9578283fd5b6020019150368190038213156130ba57600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18336030181126136e4578182fd5b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea18336030181126136e4578182fd5b60405181810167ffffffffffffffff81118282101715613e6057fe5b604052919050565b60008085851115613e77578182fd5b83861115613e83578182fd5b5050820193919092039150565b60005b83811015613eab578181015183820152602001613e93565b83811115611b295750506000910152565b73ffffffffffffffffffffffffffffffffffffffff81168114613ede57600080fd5b50565b8015158114613ede57600080fdfea2646970667358221220de5e493c48a3b42da03a5db89085177b8d8ccec6e9bf6e8e48b3809343624c8f64736f6c63430007060033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000002c4c28ddbdac9c5e7055b4c863b72ea0149d8afe000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
-----Decoded View---------------
Arg [0] : authenticator_ (address): 0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE
Arg [1] : vault_ (address): 0xBA12222222228d8Ba445958a75a0704d566BF2C8
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000002c4c28ddbdac9c5e7055b4c863b72ea0149d8afe
Arg [1] : 000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.