Feature Tip: Add private address tag to any address under My Name Tag !
More Info
Private Name Tags
ContractCreator
Latest 13 from a total of 13 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x00000000 | 20278104 | 165 days ago | IN | 0 ETH | 0.0001703 | ||||
0x00000000 | 20278101 | 165 days ago | IN | 0 ETH | 0.00017829 | ||||
0x00000000 | 20278003 | 165 days ago | IN | 0 ETH | 0.00013738 | ||||
0x00000000 | 20277999 | 165 days ago | IN | 0 ETH | 0.00014586 | ||||
Approve | 16327434 | 719 days ago | IN | 0 ETH | 0.0004998 | ||||
Approve | 16327434 | 719 days ago | IN | 0 ETH | 0.0004998 | ||||
Approve | 16327434 | 719 days ago | IN | 0 ETH | 0.0004998 | ||||
Approve | 16327428 | 719 days ago | IN | 0 ETH | 0.00049573 | ||||
Transfer | 14586812 | 983 days ago | IN | 0.002 ETH | 0.00098526 | ||||
Transfer | 14567451 | 986 days ago | IN | 0.003 ETH | 0.00095238 | ||||
Transfer | 14567449 | 986 days ago | IN | 0.004 ETH | 0.00080686 | ||||
Transfer | 14567443 | 986 days ago | IN | 0.003 ETH | 0.00074345 | ||||
Transfer | 13759540 | 1111 days ago | IN | 0.015 ETH | 0.0022588 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
12593265 | 1293 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
GPv2VaultRelayer
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 "./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; import "./interfaces/GPv2Authentication.sol"; import "./libraries/GPv2EIP1967.sol"; import "./mixins/Initializable.sol"; import "./mixins/StorageAccessible.sol"; /// @title Gnosis Protocol v2 Access Control Contract /// @author Gnosis Developers contract GPv2AllowListAuthentication is GPv2Authentication, Initializable, StorageAccessible { /// @dev The address of the manager that has permissions to add and remove /// solvers. address public manager; /// @dev The set of allowed solvers. Allowed solvers have a value of `true` /// in this mapping. mapping(address => bool) private solvers; /// @dev Event emitted when the manager changes. event ManagerChanged(address newManager, address oldManager); /// @dev Event emitted when a solver gets added. event SolverAdded(address solver); /// @dev Event emitted when a solver gets removed. event SolverRemoved(address solver); /// @dev Initialize the manager to a value. /// /// This method is a contract initializer that is called exactly once after /// creation. An initializer is used instead of a constructor so that this /// contract can be used behind a proxy. /// /// This initializer is idempotent. /// /// @param manager_ The manager to initialize the contract with. function initializeManager(address manager_) external initializer { manager = manager_; emit ManagerChanged(manager_, address(0)); } /// @dev Modifier that ensures a method can only be called by the contract /// manager. Reverts if called by other addresses. modifier onlyManager() { require(manager == msg.sender, "GPv2: caller not manager"); _; } /// @dev Modifier that ensures method can be either called by the contract /// manager or the proxy owner. /// /// This modifier assumes that the proxy uses an EIP-1967 compliant storage /// slot for the admin. modifier onlyManagerOrOwner() { require( manager == msg.sender || GPv2EIP1967.getAdmin() == msg.sender, "GPv2: not authorized" ); _; } /// @dev Set the manager for this contract. /// /// This method can be called by the current manager (if they want to to /// reliquish the role and give it to another address) or the contract /// owner (i.e. the proxy admin). /// /// @param manager_ The new contract manager address. function setManager(address manager_) external onlyManagerOrOwner { address oldManager = manager; manager = manager_; emit ManagerChanged(manager_, oldManager); } /// @dev Add an address to the set of allowed solvers. This method can only /// be called by the contract manager. /// /// This function is idempotent. /// /// @param solver The solver address to add. function addSolver(address solver) external onlyManager { solvers[solver] = true; emit SolverAdded(solver); } /// @dev Removes an address to the set of allowed solvers. This method can /// only be called by the contract manager. /// /// This function is idempotent. /// /// @param solver The solver address to remove. function removeSolver(address solver) external onlyManager { solvers[solver] = false; emit SolverRemoved(solver); } /// @inheritdoc GPv2Authentication function isSolver(address prospectiveSolver) external view override returns (bool) { return solvers[prospectiveSolver]; } }
// 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 GPv2EIP1967 { /// @dev The storage slot where the proxy administrator is stored, defined /// as `keccak256('eip1967.proxy.admin') - 1`. bytes32 internal constant ADMIN_SLOT = hex"b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"; /// @dev Returns the address stored in the EIP-1967 administrator storage /// slot for the current contract. If this method is not called from an /// contract behind an EIP-1967 proxy, then it will most likely return /// `address(0)`, as the implementation slot is likely to be unset. /// /// @return admin The administrator address. function getAdmin() internal view returns (address admin) { // solhint-disable-next-line no-inline-assembly assembly { admin := sload(ADMIN_SLOT) } } /// @dev Sets the storage at the EIP-1967 administrator slot to be the /// specified address. /// /// @param admin The administrator address to set. function setAdmin(address admin) internal { // solhint-disable-next-line no-inline-assembly assembly { sstore(ADMIN_SLOT, admin) } } }
// SPDX-License-Identifier: MIT // Vendored from OpenZeppelin contracts with minor modifications: // - Modified Solidity version // - Formatted code // - Shortned revert messages // - Inlined `Address.isContract` implementation // <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/proxy/Initializable.sol> pragma solidity ^0.7.6; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { require( _initializing || _isConstructor() || !_initialized, "Initializable: initialized" ); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } _; if (isTopLevelCall) { _initializing = false; } } /// @dev Returns true if and only if the function is running in the constructor function _isConstructor() private view returns (bool) { uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(address()) } return size == 0; } }
// 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) } } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.7.6; import "../GPv2AllowListAuthentication.sol"; contract GPv2AllowListAuthenticationV2 is GPv2AllowListAuthentication { function newMethod() external pure returns (uint256) { return 1337; } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.7.6; import "../GPv2AllowListAuthentication.sol"; import "../libraries/GPv2EIP1967.sol"; contract GPv2AllowListAuthenticationTestInterface is GPv2AllowListAuthentication { constructor(address owner) { GPv2EIP1967.setAdmin(owner); } }
// 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: 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"; 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-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; 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: LGPL-3.0-or-later pragma solidity ^0.7.6; pragma abicoder v2; import "../interfaces/GPv2EIP1271.sol"; import "../interfaces/IERC20.sol"; import "../libraries/GPv2Order.sol"; import "../libraries/GPv2SafeERC20.sol"; import "../libraries/SafeMath.sol"; import "../GPv2Settlement.sol"; /// @title Proof of Concept Smart Order /// @author Gnosis Developers contract SmartSellOrder is EIP1271Verifier { using GPv2Order for GPv2Order.Data; using GPv2SafeERC20 for IERC20; using SafeMath for uint256; bytes32 public constant APPDATA = keccak256("SmartSellOrder"); address public immutable owner; bytes32 public immutable domainSeparator; IERC20 public immutable sellToken; IERC20 public immutable buyToken; uint256 public immutable totalSellAmount; uint256 public immutable totalFeeAmount; uint32 public immutable validTo; constructor( GPv2Settlement settlement, IERC20 sellToken_, IERC20 buyToken_, uint32 validTo_, uint256 totalSellAmount_, uint256 totalFeeAmount_ ) { owner = msg.sender; domainSeparator = settlement.domainSeparator(); sellToken = sellToken_; buyToken = buyToken_; validTo = validTo_; totalSellAmount = totalSellAmount_; totalFeeAmount = totalFeeAmount_; sellToken_.approve( address(settlement.vaultRelayer()), type(uint256).max ); } modifier onlyOwner { require(msg.sender == owner, "not owner"); _; } function withdraw(uint256 amount) external onlyOwner { sellToken.safeTransfer(owner, amount); } function close() external onlyOwner { uint256 balance = sellToken.balanceOf(address(this)); if (balance != 0) { sellToken.safeTransfer(owner, balance); } selfdestruct(payable(owner)); } function isValidSignature(bytes32 hash, bytes memory signature) external view override returns (bytes4 magicValue) { uint256 sellAmount = abi.decode(signature, (uint256)); GPv2Order.Data memory order = orderForSellAmount(sellAmount); if (order.hash(domainSeparator) == hash) { magicValue = GPv2EIP1271.MAGICVALUE; } } function orderForSellAmount(uint256 sellAmount) public view returns (GPv2Order.Data memory order) { order.sellToken = sellToken; order.buyToken = buyToken; order.receiver = owner; order.sellAmount = sellAmount; order.buyAmount = buyAmountForSellAmount(sellAmount); order.validTo = validTo; order.appData = APPDATA; order.feeAmount = totalFeeAmount.mul(sellAmount).div(totalSellAmount); order.kind = GPv2Order.KIND_SELL; // NOTE: We counter-intuitively set `partiallyFillable` to `false`, even // if the smart order as a whole acts like a partially fillable order. // This is done since, once a settlement commits to a specific sell // amount, then it is expected to use it completely and not partially. order.partiallyFillable = false; order.sellTokenBalance = GPv2Order.BALANCE_ERC20; order.buyTokenBalance = GPv2Order.BALANCE_ERC20; } function buyAmountForSellAmount(uint256 sellAmount) private view returns (uint256 buyAmount) { uint256 feeAdjustedBalance = sellToken.balanceOf(address(this)).mul(totalSellAmount).div( totalSellAmount.add(totalFeeAmount) ); uint256 soldAmount = totalSellAmount > feeAdjustedBalance ? totalSellAmount - feeAdjustedBalance : 0; // NOTE: This is currently a silly price strategy where the xrate // increases linearly from 1:1 to 1:2 as the smart order gets filled. // This can be extended to more complex "price curves". buyAmount = sellAmount .mul(totalSellAmount.add(sellAmount).add(soldAmount)) .div(totalSellAmount); } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.7.6; import "../libraries/SafeMath.sol"; abstract contract NonStandardERC20 { using SafeMath for uint256; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; function mint(address to, uint256 amount) external { balanceOf[to] = balanceOf[to].add(amount); } function approve(address spender, uint256 amount) external { allowance[msg.sender][spender] = amount; } function transfer_(address to, uint256 amount) internal { balanceOf[msg.sender] = balanceOf[msg.sender].sub(amount); balanceOf[to] = balanceOf[to].add(amount); } function transferFrom_( address from, address to, uint256 amount ) internal { allowance[from][msg.sender] = allowance[from][msg.sender].sub(amount); balanceOf[from] = balanceOf[from].sub(amount); balanceOf[to] = balanceOf[to].add(amount); } } contract ERC20NoReturn is NonStandardERC20 { function transfer(address to, uint256 amount) external { transfer_(to, amount); } function transferFrom( address from, address to, uint256 amount ) external { transferFrom_(from, to, amount); } } contract ERC20ReturningUint is NonStandardERC20 { // Largest 256-bit prime :) uint256 private constant OK = 115792089237316195423570985008687907853269984665640564039457584007913129639747; function transfer(address to, uint256 amount) external returns (uint256) { transfer_(to, amount); return OK; } function transferFrom( address from, address to, uint256 amount ) external returns (uint256) { transferFrom_(from, to, amount); return OK; } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.7.6; pragma abicoder v2; import "../interfaces/IERC20.sol"; import "../libraries/GPv2SafeERC20.sol"; contract GPv2SafeERC20TestInterface { using GPv2SafeERC20 for IERC20; function transfer( IERC20 token, address to, uint256 value ) public { token.safeTransfer(to, value); } function transferFrom( IERC20 token, address from, address to, uint256 value ) public { token.safeTransferFrom(from, to, value); } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.7.6; pragma abicoder v2; import "../libraries/GPv2Order.sol"; import "../libraries/GPv2Trade.sol"; contract GPv2TradeTestInterface { function extractOrderTest( IERC20[] calldata tokens, GPv2Trade.Data calldata trade ) external pure returns (GPv2Order.Data memory order) { GPv2Trade.extractOrder(trade, tokens, order); } function extractFlagsTest(uint256 flags) external pure returns ( bytes32 kind, bool partiallyFillable, bytes32 sellTokenBalance, bytes32 buyTokenBalance, GPv2Signing.Scheme signingScheme ) { return GPv2Trade.extractFlags(flags); } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.7.6; pragma abicoder v2; import "../libraries/GPv2Order.sol"; import "../libraries/GPv2Trade.sol"; import "../mixins/GPv2Signing.sol"; contract GPv2SigningTestInterface is GPv2Signing { function recoverOrderFromTradeTest( IERC20[] calldata tokens, GPv2Trade.Data calldata trade ) external view returns (RecoveredOrder memory recoveredOrder) { recoveredOrder = allocateRecoveredOrder(); recoverOrderFromTrade(recoveredOrder, tokens, trade); } function recoverOrderSignerTest( GPv2Order.Data memory order, GPv2Signing.Scheme signingScheme, bytes calldata signature ) external view returns (address owner) { (, owner) = recoverOrderSigner(order, signingScheme, signature); } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.7.6; import "../interfaces/GPv2EIP1271.sol"; /// @dev This contract implements the standard described in EIP-1271 with the /// minor change that the verification function changes the state. This is /// forbidden by the standard specifications. contract StateChangingEIP1271 { uint256 public state = 0; // solhint-disable-next-line no-unused-vars function isValidSignature(bytes32 _hash, bytes memory _signature) public returns (bytes4 magicValue) { state += 1; magicValue = GPv2EIP1271.MAGICVALUE; // The following lines are here to suppress no-unused-var compiler-time // warnings when compiling the contracts. The warning is forwarded by // Hardhat from Solc. It is currently not possible to selectively // ignore Solc warinings: // <https://github.com/ethereum/solidity/issues/269> _hash; _signature; } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.7.6; pragma abicoder v2; import "../libraries/GPv2Order.sol"; contract GPv2OrderTestInterface { using GPv2Order for GPv2Order.Data; using GPv2Order for bytes; function typeHashTest() external pure returns (bytes32) { return GPv2Order.TYPE_HASH; } function hashTest(GPv2Order.Data memory order, bytes32 domainSeparator) external pure returns (bytes32 orderDigest) { orderDigest = order.hash(domainSeparator); } function packOrderUidParamsTest( uint256 bufferLength, bytes32 orderDigest, address owner, uint32 validTo ) external pure returns (bytes memory orderUid) { orderUid = new bytes(bufferLength); orderUid.packOrderUidParams(orderDigest, owner, validTo); } function extractOrderUidParamsTest(bytes calldata orderUid) external pure returns ( bytes32 orderDigest, address owner, uint32 validTo ) { return orderUid.extractOrderUidParams(); } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.7.6; pragma abicoder v2; import "../libraries/GPv2Transfer.sol"; contract GPv2TransferTestInterface { function fastTransferFromAccountTest( IVault vault, GPv2Transfer.Data calldata transfer, address recipient ) external { GPv2Transfer.fastTransferFromAccount(vault, transfer, recipient); } function transferFromAccountsTest( IVault vault, GPv2Transfer.Data[] calldata transfers, address recipient ) external { GPv2Transfer.transferFromAccounts(vault, transfers, recipient); } function transferToAccountsTest( IVault vault, GPv2Transfer.Data[] memory transfers ) external { GPv2Transfer.transferToAccounts(vault, transfers); } // solhint-disable-next-line no-empty-blocks receive() external payable {} }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.7.6; pragma abicoder v2; import "../GPv2Settlement.sol"; import "../libraries/GPv2Interaction.sol"; import "../libraries/GPv2Trade.sol"; import "../libraries/GPv2Transfer.sol"; contract GPv2SettlementTestInterface is GPv2Settlement { constructor(GPv2Authentication authenticator_, IVault vault) GPv2Settlement(authenticator_, vault) // solhint-disable-next-line no-empty-blocks { } function setFilledAmount(bytes calldata orderUid, uint256 amount) external { filledAmount[orderUid] = amount; } function computeTradeExecutionsTest( IERC20[] calldata tokens, uint256[] calldata clearingPrices, GPv2Trade.Data[] calldata trades ) external returns ( GPv2Transfer.Data[] memory inTransfers, GPv2Transfer.Data[] memory outTransfers ) { (inTransfers, outTransfers) = computeTradeExecutions( tokens, clearingPrices, trades ); } function computeTradeExecutionMemoryTest() external returns (uint256 mem) { RecoveredOrder memory recoveredOrder; GPv2Transfer.Data memory inTransfer; GPv2Transfer.Data memory outTransfer; // NOTE: Solidity stores the free memory pointer at address 0x40. Read // it before and after calling `processOrder` to ensure that there are // no memory allocations. // solhint-disable-next-line no-inline-assembly assembly { mem := mload(0x40) } // solhint-disable-next-line not-rely-on-time recoveredOrder.data.validTo = uint32(block.timestamp); computeTradeExecution(recoveredOrder, 1, 1, 0, inTransfer, outTransfer); // solhint-disable-next-line no-inline-assembly assembly { mem := sub(mload(0x40), mem) } } function executeInteractionsTest( GPv2Interaction.Data[] calldata interactions ) external { executeInteractions(interactions); } function freeFilledAmountStorageTest(bytes[] calldata orderUids) external { this.freeFilledAmountStorage(orderUids); } function freePreSignatureStorageTest(bytes[] calldata orderUids) external { this.freePreSignatureStorage(orderUids); } }
// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.7.6; pragma abicoder v2; import "../libraries/GPv2Interaction.sol"; contract GPv2InteractionTestInterface { // solhint-disable-next-line no-empty-blocks receive() external payable {} function executeTest(GPv2Interaction.Data calldata interaction) external { GPv2Interaction.execute(interaction); } function selectorTest(GPv2Interaction.Data calldata interaction) external pure returns (bytes4) { return GPv2Interaction.selector(interaction); } }
{ "optimizer": { "enabled": true, "runs": 1000000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "metadata": { "useLiteralContent": true } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract IVault","name":"vault_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"enum IVault.SwapKind","name":"kind","type":"uint8"},{"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":"address","name":"sender","type":"address"},{"internalType":"bool","name":"fromInternalBalance","type":"bool"},{"internalType":"address payable","name":"recipient","type":"address"},{"internalType":"bool","name":"toInternalBalance","type":"bool"}],"internalType":"struct IVault.FundManagement","name":"funds","type":"tuple"},{"internalType":"int256[]","name":"limits","type":"int256[]"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"balance","type":"bytes32"}],"internalType":"struct GPv2Transfer.Data","name":"feeTransfer","type":"tuple"}],"name":"batchSwapWithFee","outputs":[{"internalType":"int256[]","name":"tokenDeltas","type":"int256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"balance","type":"bytes32"}],"internalType":"struct GPv2Transfer.Data[]","name":"transfers","type":"tuple[]"}],"name":"transferFromAccounts","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60c060405234801561001057600080fd5b5060405161129e38038061129e83398101604081905261002f9161004b565b33606090811b6080521b6001600160601b03191660a052610079565b60006020828403121561005c578081fd5b81516001600160a01b0381168114610072578182fd5b9392505050565b60805160601c60a05160601c6111ee6100b060003980610130528061020152806102bd5250806093528061024c52506111ee6000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80634817a2861461003b5780637d10d11f14610064575b600080fd5b61004e610049366004610cd9565b610079565b60405161005b9190610eb3565b60405180910390f35b610077610072366004610c69565b610234565b005b60603373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146100f3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ea906110e5565b60405180910390fd5b6040517f945bcec900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063945bcec990610171908c908c908c908c908c908c908c90600401610f59565b600060405180830381600087803b15801561018b57600080fd5b505af115801561019f573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526101e59190810190610bd9565b905061022873ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001683336102e9565b98975050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146102a3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ea906110e5565b6102e573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016838333610551565b5050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61030e6040840160208501610bb6565b73ffffffffffffffffffffffffffffffffffffffff16141561035c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ea9061111c565b7f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9826060013514156103d0576103cb6103986020840184610bb6565b82604085018035906103ad9060208801610bb6565b73ffffffffffffffffffffffffffffffffffffffff16929190610816565b61054c565b604080516001808252818301909252600091816020015b6103ef6109cb565b8152602001906001900390816103e757905050905060008160008151811061041357fe5b602002602001015190507fabee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea063284606001351461044f576002610452565b60035b8190600381111561045f57fe5b9081600381111561046c57fe5b90525061047f6040850160208601610bb6565b73ffffffffffffffffffffffffffffffffffffffff16602080830191909152604080860135908301526104b490850185610bb6565b73ffffffffffffffffffffffffffffffffffffffff908116606083015283811660808301526040517f0e8e3e8400000000000000000000000000000000000000000000000000000000815290861690630e8e3e8490610517908590600401610ec6565b600060405180830381600087803b15801561053157600080fd5b505af1158015610545573d6000803e3d6000fd5b5050505050505b505050565b60008267ffffffffffffffff8111801561056a57600080fd5b506040519080825280602002602001820160405280156105a457816020015b6105916109cb565b8152602001906001900390816105895790505b5090506000805b8481101561077857368686838181106105c057fe5b60800291909101915073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee90506105f06040830160208401610bb6565b73ffffffffffffffffffffffffffffffffffffffff16141561063e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ea9061111c565b7f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9816060013514156106945761068f61067a6020830183610bb6565b86604084018035906103ad9060208701610bb6565b61076f565b60008484806001019550815181106106a857fe5b602002602001015190507fabee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea06328260600135146106e45760016106e7565b60035b819060038111156106f457fe5b9081600381111561070157fe5b9052506107146040830160208401610bb6565b73ffffffffffffffffffffffffffffffffffffffff166020808301919091526040808401359083015261074990830183610bb6565b73ffffffffffffffffffffffffffffffffffffffff908116606083015286166080909101525b506001016105ab565b50801561080e5761078982826108fd565b6040517f0e8e3e8400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871690630e8e3e84906107db908590600401610ec6565b600060405180830381600087803b1580156107f557600080fd5b505af1158015610809573d6000803e3d6000fd5b505050505b505050505050565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000080825273ffffffffffffffffffffffffffffffffffffffff8581166004840152841660248301526044820183905290600080606483828a5af1610881573d6000803e3d6000fd5b5061088b85610901565b6108f657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f475076323a206661696c6564207472616e7366657246726f6d00000000000000604482015290519081900360640190fd5b5050505050565b9052565b600061093f565b7f08c379a0000000000000000000000000000000000000000000000000000000006000526020600452806024528160445260646000fd5b3d801561097e57602081146109b8576109797f475076323a206d616c666f726d6564207472616e7366657220726573756c7400601f610908565b6109c5565b823b6109af576109af7f475076323a206e6f74206120636f6e74726163740000000000000000000000006014610908565b600191506109c5565b3d6000803e600051151591505b50919050565b6040805160a081019091528060008152600060208201819052604082018190526060820181905260809091015290565b600082601f830112610a0b578081fd5b81356020610a20610a1b83611175565b611151565b8281528181019085830183850287018401881015610a3c578586fd5b855b85811015610a63578135610a5181611193565b84529284019290840190600101610a3e565b5090979650505050505050565b600082601f830112610a80578081fd5b81356020610a90610a1b83611175565b8281528181019085830183850287018401881015610aac578586fd5b855b85811015610a6357813584529284019290840190600101610aae565b60008083601f840112610adb578182fd5b50813567ffffffffffffffff811115610af2578182fd5b6020830191508360208083028501011115610b0c57600080fd5b9250929050565b80358015158114610b2357600080fd5b919050565b6000608082840312156109c5578081fd5b600060808284031215610b4a578081fd5b6040516080810181811067ffffffffffffffff82111715610b6757fe5b6040529050808235610b7881611193565b8152610b8660208401610b13565b60208201526040830135610b9981611193565b6040820152610baa60608401610b13565b60608201525092915050565b600060208284031215610bc7578081fd5b8135610bd281611193565b9392505050565b60006020808385031215610beb578182fd5b825167ffffffffffffffff811115610c01578283fd5b8301601f81018513610c11578283fd5b8051610c1f610a1b82611175565b8181528381019083850185840285018601891015610c3b578687fd5b8694505b83851015610c5d578051835260019490940193918501918501610c3f565b50979650505050505050565b60008060208385031215610c7b578081fd5b823567ffffffffffffffff80821115610c92578283fd5b818501915085601f830112610ca5578283fd5b813581811115610cb3578384fd5b866020608083028501011115610cc7578384fd5b60209290920196919550909350505050565b6000806000806000806000806101a0898b031215610cf5578384fd5b883560028110610d03578485fd5b9750602089013567ffffffffffffffff80821115610d1f578586fd5b610d2b8c838d01610aca565b909950975060408b0135915080821115610d43578586fd5b610d4f8c838d016109fb565b9650610d5e8c60608d01610b39565b955060e08b0135915080821115610d73578485fd5b50610d808b828c01610a70565b9350506101008901359150610d998a6101208b01610b28565b90509295985092959890939650565b6000815180845260208085019450808401835b83811015610ded57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101610dbb565b509495945050505050565b6000815180845260208085019450808401835b83811015610ded57815187529582019590820190600101610e0b565b600082845282826020860137806020848601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011685010190509392505050565b73ffffffffffffffffffffffffffffffffffffffff808251168352602082015115156020840152806040830151166040840152506060810151151560608301525050565b600060208252610bd26020830184610df8565b602080825282518282018190526000919060409081850190868401855b82811015610f4c578151805160048110610ef957fe5b85528087015173ffffffffffffffffffffffffffffffffffffffff908116888701528682015187870152606080830151821690870152608091820151169085015260a09093019290850190600101610ee3565b5091979650505050505050565b600061012080830160028b10610f6b57fe5b8a8452602080850192909252889052610140808401918981028501909101908a845b8b811015611098577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec087850301855281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618e3603018112610fed578687fd5b8d01803585528381013584860152604080820135908601526060808201359086015260a0608080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe101811261104757898afd5b8301803567ffffffffffffffff81111561105f578a8bfd5b80360385131561106d578a8bfd5b83838a0152611081848a01828a8501610e27565b998801999850505093850193505050600101610f8d565b50505083810360408501526110ad8189610da8565b9150506110bd6060840187610e6f565b82810360e08401526110cf8186610df8565b9150508261010083015298975050505050505050565b60208082526011908201527f475076323a206e6f742063726561746f72000000000000000000000000000000604082015260600190565b6020808252818101527f475076323a2063616e6e6f74207472616e73666572206e617469766520455448604082015260600190565b60405181810167ffffffffffffffff8111828210171561116d57fe5b604052919050565b600067ffffffffffffffff82111561118957fe5b5060209081020190565b73ffffffffffffffffffffffffffffffffffffffff811681146111b557600080fd5b5056fea2646970667358221220364a6941bea69620b7dc3a957d0ab4cbf3bfc459c7ad3924d220620aca9202fc64736f6c63430007060033000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100365760003560e01c80634817a2861461003b5780637d10d11f14610064575b600080fd5b61004e610049366004610cd9565b610079565b60405161005b9190610eb3565b60405180910390f35b610077610072366004610c69565b610234565b005b60603373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000009008d19f58aabd9ed0d60971565aa8510560ab4116146100f3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ea906110e5565b60405180910390fd5b6040517f945bcec900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8169063945bcec990610171908c908c908c908c908c908c908c90600401610f59565b600060405180830381600087803b15801561018b57600080fd5b505af115801561019f573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526101e59190810190610bd9565b905061022873ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c81683336102e9565b98975050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000009008d19f58aabd9ed0d60971565aa8510560ab4116146102a3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ea906110e5565b6102e573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c816838333610551565b5050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61030e6040840160208501610bb6565b73ffffffffffffffffffffffffffffffffffffffff16141561035c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ea9061111c565b7f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9826060013514156103d0576103cb6103986020840184610bb6565b82604085018035906103ad9060208801610bb6565b73ffffffffffffffffffffffffffffffffffffffff16929190610816565b61054c565b604080516001808252818301909252600091816020015b6103ef6109cb565b8152602001906001900390816103e757905050905060008160008151811061041357fe5b602002602001015190507fabee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea063284606001351461044f576002610452565b60035b8190600381111561045f57fe5b9081600381111561046c57fe5b90525061047f6040850160208601610bb6565b73ffffffffffffffffffffffffffffffffffffffff16602080830191909152604080860135908301526104b490850185610bb6565b73ffffffffffffffffffffffffffffffffffffffff908116606083015283811660808301526040517f0e8e3e8400000000000000000000000000000000000000000000000000000000815290861690630e8e3e8490610517908590600401610ec6565b600060405180830381600087803b15801561053157600080fd5b505af1158015610545573d6000803e3d6000fd5b5050505050505b505050565b60008267ffffffffffffffff8111801561056a57600080fd5b506040519080825280602002602001820160405280156105a457816020015b6105916109cb565b8152602001906001900390816105895790505b5090506000805b8481101561077857368686838181106105c057fe5b60800291909101915073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee90506105f06040830160208401610bb6565b73ffffffffffffffffffffffffffffffffffffffff16141561063e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ea9061111c565b7f5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9816060013514156106945761068f61067a6020830183610bb6565b86604084018035906103ad9060208701610bb6565b61076f565b60008484806001019550815181106106a857fe5b602002602001015190507fabee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea06328260600135146106e45760016106e7565b60035b819060038111156106f457fe5b9081600381111561070157fe5b9052506107146040830160208401610bb6565b73ffffffffffffffffffffffffffffffffffffffff166020808301919091526040808401359083015261074990830183610bb6565b73ffffffffffffffffffffffffffffffffffffffff908116606083015286166080909101525b506001016105ab565b50801561080e5761078982826108fd565b6040517f0e8e3e8400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871690630e8e3e84906107db908590600401610ec6565b600060405180830381600087803b1580156107f557600080fd5b505af1158015610809573d6000803e3d6000fd5b505050505b505050505050565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000080825273ffffffffffffffffffffffffffffffffffffffff8581166004840152841660248301526044820183905290600080606483828a5af1610881573d6000803e3d6000fd5b5061088b85610901565b6108f657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f475076323a206661696c6564207472616e7366657246726f6d00000000000000604482015290519081900360640190fd5b5050505050565b9052565b600061093f565b7f08c379a0000000000000000000000000000000000000000000000000000000006000526020600452806024528160445260646000fd5b3d801561097e57602081146109b8576109797f475076323a206d616c666f726d6564207472616e7366657220726573756c7400601f610908565b6109c5565b823b6109af576109af7f475076323a206e6f74206120636f6e74726163740000000000000000000000006014610908565b600191506109c5565b3d6000803e600051151591505b50919050565b6040805160a081019091528060008152600060208201819052604082018190526060820181905260809091015290565b600082601f830112610a0b578081fd5b81356020610a20610a1b83611175565b611151565b8281528181019085830183850287018401881015610a3c578586fd5b855b85811015610a63578135610a5181611193565b84529284019290840190600101610a3e565b5090979650505050505050565b600082601f830112610a80578081fd5b81356020610a90610a1b83611175565b8281528181019085830183850287018401881015610aac578586fd5b855b85811015610a6357813584529284019290840190600101610aae565b60008083601f840112610adb578182fd5b50813567ffffffffffffffff811115610af2578182fd5b6020830191508360208083028501011115610b0c57600080fd5b9250929050565b80358015158114610b2357600080fd5b919050565b6000608082840312156109c5578081fd5b600060808284031215610b4a578081fd5b6040516080810181811067ffffffffffffffff82111715610b6757fe5b6040529050808235610b7881611193565b8152610b8660208401610b13565b60208201526040830135610b9981611193565b6040820152610baa60608401610b13565b60608201525092915050565b600060208284031215610bc7578081fd5b8135610bd281611193565b9392505050565b60006020808385031215610beb578182fd5b825167ffffffffffffffff811115610c01578283fd5b8301601f81018513610c11578283fd5b8051610c1f610a1b82611175565b8181528381019083850185840285018601891015610c3b578687fd5b8694505b83851015610c5d578051835260019490940193918501918501610c3f565b50979650505050505050565b60008060208385031215610c7b578081fd5b823567ffffffffffffffff80821115610c92578283fd5b818501915085601f830112610ca5578283fd5b813581811115610cb3578384fd5b866020608083028501011115610cc7578384fd5b60209290920196919550909350505050565b6000806000806000806000806101a0898b031215610cf5578384fd5b883560028110610d03578485fd5b9750602089013567ffffffffffffffff80821115610d1f578586fd5b610d2b8c838d01610aca565b909950975060408b0135915080821115610d43578586fd5b610d4f8c838d016109fb565b9650610d5e8c60608d01610b39565b955060e08b0135915080821115610d73578485fd5b50610d808b828c01610a70565b9350506101008901359150610d998a6101208b01610b28565b90509295985092959890939650565b6000815180845260208085019450808401835b83811015610ded57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101610dbb565b509495945050505050565b6000815180845260208085019450808401835b83811015610ded57815187529582019590820190600101610e0b565b600082845282826020860137806020848601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011685010190509392505050565b73ffffffffffffffffffffffffffffffffffffffff808251168352602082015115156020840152806040830151166040840152506060810151151560608301525050565b600060208252610bd26020830184610df8565b602080825282518282018190526000919060409081850190868401855b82811015610f4c578151805160048110610ef957fe5b85528087015173ffffffffffffffffffffffffffffffffffffffff908116888701528682015187870152606080830151821690870152608091820151169085015260a09093019290850190600101610ee3565b5091979650505050505050565b600061012080830160028b10610f6b57fe5b8a8452602080850192909252889052610140808401918981028501909101908a845b8b811015611098577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec087850301855281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618e3603018112610fed578687fd5b8d01803585528381013584860152604080820135908601526060808201359086015260a0608080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe101811261104757898afd5b8301803567ffffffffffffffff81111561105f578a8bfd5b80360385131561106d578a8bfd5b83838a0152611081848a01828a8501610e27565b998801999850505093850193505050600101610f8d565b50505083810360408501526110ad8189610da8565b9150506110bd6060840187610e6f565b82810360e08401526110cf8186610df8565b9150508261010083015298975050505050505050565b60208082526011908201527f475076323a206e6f742063726561746f72000000000000000000000000000000604082015260600190565b6020808252818101527f475076323a2063616e6e6f74207472616e73666572206e617469766520455448604082015260600190565b60405181810167ffffffffffffffff8111828210171561116d57fe5b604052919050565b600067ffffffffffffffff82111561118957fe5b5060209081020190565b73ffffffffffffffffffffffffffffffffffffffff811681146111b557600080fd5b5056fea2646970667358221220364a6941bea69620b7dc3a957d0ab4cbf3bfc459c7ad3924d220620aca9202fc64736f6c63430007060033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
-----Decoded View---------------
Arg [0] : vault_ (address): 0xBA12222222228d8Ba445958a75a0704d566BF2C8
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
Deployed Bytecode Sourcemap
282:2656:2:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2348:588;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;1218:177;;;;;;:::i;:::-;;:::i;:::-;;2348:588;2669:27;849:10;:21;863:7;849:21;;841:51;;;;;;;;;;;;:::i;:::-;;;;;;;;;2722:143:::1;::::0;;;;:15:::1;:5;:15;::::0;::::1;::::0;:143:::1;::::0;2751:4;;2769:5;;;;2788:6;;2808:5;;2827:6;;2847:8;;2722:143:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;::::0;;::::1;::::0;::::1;::::0;::::1;;::::0;::::1;::::0;;;::::1;::::0;::::1;:::i;:::-;2708:157:::0;-1:-1:-1;2875:54:2::1;:29;:5;:29;2905:11:::0;2918:10:::1;2875:29;:54::i;:::-;2348:588:::0;;;;;;;;;;:::o;1218:177::-;849:10;:21;863:7;849:21;;841:51;;;;;;;;;;;;:::i;:::-;1339:49:::1;:26;:5;:26;1366:9:::0;;1377:10:::1;1339:26;:49::i;:::-;1218:177:::0;;:::o;1779:1123:12:-;623:42;1947:14;;;;;;;;:::i;:::-;1939:42;;;;1918:121;;;;;;;;;;;;:::i;:::-;2074:23;2054:8;:16;;;:43;2050:846;;;2113:139;2162:16;;;;:8;:16;:::i;:::-;2196:9;2223:15;;;;;;2113:14;;;;;;:::i;:::-;:31;;;:139;;:31;:139::i;:::-;2050:846;;;2342:29;;;2369:1;2342:29;;;;;;;;;2283:40;;2342:29;;;;;;:::i;:::-;;;;;;;;;;;;;;;;2283:88;;2386:37;2426:10;2437:1;2426:13;;;;;;;;;;;;;;2386:53;;2490:26;2470:8;:16;;;:46;:168;;2596:42;2470:168;;;2535:42;2470:168;2453:9;;:185;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2670:14:12;;;;;;;;:::i;:::-;2652:32;;:15;;;;:32;;;;2717:15;;;;;2698:16;;;:34;2765:16;;;;2717:8;2765:16;:::i;:::-;2746:35;;;;:16;;;:35;2795:40;;;:19;;;:40;2850:35;;;;;:23;;;;;;:35;;2874:10;;2850:35;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2050:846;;;1779:1123;;;:::o;3499:1781::-;3911:40;3993:9;3966:44;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;3911:99;;4020:22;4062:9;4057:1058;4077:20;;;4057:1058;;;4118:22;4143:9;;4153:1;4143:12;;;;;;;;;;;;;;-1:-1:-1;623:42:12;;-1:-1:-1;4202:14:12;;;;;;;;:::i;:::-;4194:42;;;;4169:133;;;;;;;;;;;;:::i;:::-;4341:23;4321:8;:16;;;:43;4317:788;;;4384:155;4437:16;;;;:8;:16;:::i;:::-;4475:9;4506:15;;;;;;4384:14;;;;;;:::i;:155::-;4317:788;;;4578:37;4638:10;4649:16;;;;;;4638:28;;;;;;;;;;;;;;4578:88;;4721:26;4701:8;:16;;;:46;:176;;4835:42;4701:176;;;4770:42;4701:176;4684:9;;:193;;;;;;;;;;;;;;;;;;;;-1:-1:-1;4913:14:12;;;;;;;;:::i;:::-;4895:32;;:15;;;;:32;;;;4964:15;;;;;4945:16;;;:34;5016:16;;;;4964:8;5016:16;:::i;:::-;4997:35;;;;:16;;;:35;5050:40;;:19;;;;:40;4317:788;-1:-1:-1;4099:3:12;;4057:1058;;;-1:-1:-1;5129:18:12;;5125:149;;5163:51;5187:10;5199:14;5163:23;:51::i;:::-;5228:35;;;;;:23;;;;;;:35;;5252:10;;5228:35;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5125:149;3499:1781;;;;;;:::o;1421:989:10:-;1726:4;1720:11;1578:27;1744:36;;;1870:42;1860:53;;;1840:1;1817:25;;1793:134;2008:51;;1987:2;1964:26;;1940:133;2116:2;2093:26;;2086:41;;;1578:27;1559:16;;2192:3;1720:11;1559:16;2163:5;2156;2151:51;2141:2;;2243:16;2240:1;2237;2222:38;2287:16;2284:1;2277:27;2141:2;;2346:27;2367:5;2346:20;:27::i;:::-;2338:65;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1421:989;;;;;:::o;7386:578:12:-;7919:29;;7905:53::o;2608:3005:10:-;2698:12;3870:253;;;3945:18;3939:4;3932:32;3994:4;3988;3981:18;4029:6;4023:4;4016:20;4066:7;4060:4;4053:21;4104:4;4098;4091:18;3914:209;4144:16;4240:549;;;;4887:2;4882:594;;;;5523:56;5545:33;5541:2;5523:56;:::i;:::-;4137:1460;;4240:549;4636:5;4624:18;4614:2;;4670:45;4692:22;4688:2;4670:45;:::i;:::-;4770:1;4759:12;;4240:549;;4882:594;4933:16;4930:1;4927;4912:38;5454:1;5448:8;5441:16;5434:24;5423:35;;4137:1460;;3210:2397;;;:::o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;14:776:31:-;;124:3;117:4;109:6;105:17;101:27;91:2;;146:5;139;132:20;91:2;186:6;173:20;212:4;236:62;251:46;294:2;251:46;:::i;:::-;236:62;:::i;:::-;332:15;;;363:12;;;;395:15;;;441:11;;;429:24;;425:33;;422:42;-1:-1:-1;419:2:31;;;481:5;474;467:20;419:2;507:5;521:240;535:2;532:1;529:9;521:240;;;606:3;593:17;623:33;650:5;623:33;:::i;:::-;669:18;;707:12;;;;739;;;;553:1;546:9;521:240;;;-1:-1:-1;779:5:31;;81:709;-1:-1:-1;;;;;;;81:709:31:o;795:701::-;;907:3;900:4;892:6;888:17;884:27;874:2;;929:5;922;915:20;874:2;969:6;956:20;995:4;1019:62;1034:46;1077:2;1034:46;:::i;1019:62::-;1115:15;;;1146:12;;;;1178:15;;;1224:11;;;1212:24;;1208:33;;1205:42;-1:-1:-1;1202:2:31;;;1264:5;1257;1250:20;1202:2;1290:5;1304:163;1318:2;1315:1;1312:9;1304:163;;;1375:17;;1363:30;;1413:12;;;;1445;;;;1336:1;1329:9;1304:163;;1501:427;;;1657:3;1650:4;1642:6;1638:17;1634:27;1624:2;;1682:8;1672;1665:26;1624:2;-1:-1:-1;1712:20:31;;1755:18;1744:30;;1741:2;;;1794:8;1784;1777:26;1741:2;1838:4;1830:6;1826:17;1814:29;;1901:3;1894:4;1886;1878:6;1874:17;1866:6;1862:30;1858:41;1855:50;1852:2;;;1918:1;1915;1908:12;1852:2;1614:314;;;;;:::o;1933:162::-;2000:20;;2056:13;;2049:21;2039:32;;2029:2;;2085:1;2082;2075:12;2029:2;1981:114;;;:::o;2100:164::-;;2205:3;2196:6;2191:3;2187:16;2183:26;2180:2;;;2226:5;2219;2212:20;2269:773;;2381:4;2369:9;2364:3;2360:19;2356:30;2353:2;;;2403:5;2396;2389:20;2353:2;2440;2434:9;2482:4;2474:6;2470:17;2553:6;2541:10;2538:22;2517:18;2505:10;2502:34;2499:62;2496:2;;;2564:9;2496:2;2591;2584:22;2624:6;-1:-1:-1;2624:6:31;2654:23;;2686:35;2654:23;2686:35;:::i;:::-;2730:23;;2786:37;2819:2;2804:18;;2786:37;:::i;:::-;2781:2;2773:6;2769:15;2762:62;2876:2;2865:9;2861:18;2848:32;2889:35;2916:7;2889:35;:::i;:::-;2952:2;2940:15;;2933:32;2998:37;3031:2;3016:18;;2998:37;:::i;:::-;2993:2;2985:6;2981:15;2974:62;;2343:699;;;;:::o;3047:259::-;;3159:2;3147:9;3138:7;3134:23;3130:32;3127:2;;;3180:6;3172;3165:22;3127:2;3224:9;3211:23;3243:33;3270:5;3243:33;:::i;:::-;3295:5;3117:189;-1:-1:-1;;;3117:189:31:o;3311:939::-;;3436:2;3479;3467:9;3458:7;3454:23;3450:32;3447:2;;;3500:6;3492;3485:22;3447:2;3538:9;3532:16;3571:18;3563:6;3560:30;3557:2;;;3608:6;3600;3593:22;3557:2;3636:22;;3689:4;3681:13;;3677:27;-1:-1:-1;3667:2:31;;3723:6;3715;3708:22;3667:2;3757;3751:9;3780:62;3795:46;3838:2;3795:46;:::i;3780:62::-;3876:15;;;3907:12;;;;3939:11;;;3977;;;3969:20;;3965:29;;3962:42;-1:-1:-1;3959:2:31;;;4022:6;4014;4007:22;3959:2;4049:6;4040:15;;4064:156;4078:2;4075:1;4072:9;4064:156;;;4135:10;;4123:23;;4096:1;4089:9;;;;;4166:12;;;;4198;;4064:156;;;-1:-1:-1;4239:5:31;3416:834;-1:-1:-1;;;;;;;3416:834:31:o;4255:692::-;;;4426:2;4414:9;4405:7;4401:23;4397:32;4394:2;;;4447:6;4439;4432:22;4394:2;4492:9;4479:23;4521:18;4562:2;4554:6;4551:14;4548:2;;;4583:6;4575;4568:22;4548:2;4626:6;4615:9;4611:22;4601:32;;4671:7;4664:4;4660:2;4656:13;4652:27;4642:2;;4698:6;4690;4683:22;4642:2;4743;4730:16;4769:2;4761:6;4758:14;4755:2;;;4790:6;4782;4775:22;4755:2;4851:7;4846:2;4838:4;4830:6;4826:17;4822:2;4818:26;4814:35;4811:48;4808:2;;;4877:6;4869;4862:22;4808:2;4913;4905:11;;;;;4935:6;;-1:-1:-1;4384:563:31;;-1:-1:-1;;;;4384:563:31:o;5231:1528::-;;;;;;;;;5646:3;5634:9;5625:7;5621:23;5617:33;5614:2;;;5668:6;5660;5653:22;5614:2;5712:9;5699:23;5751:1;5744:5;5741:12;5731:2;;5772:6;5764;5757:22;5731:2;5800:5;-1:-1:-1;5856:2:31;5841:18;;5828:32;5879:18;5909:14;;;5906:2;;;5941:6;5933;5926:22;5906:2;5985:99;6076:7;6067:6;6056:9;6052:22;5985:99;:::i;:::-;6103:8;;-1:-1:-1;5959:125:31;-1:-1:-1;6191:2:31;6176:18;;6163:32;;-1:-1:-1;6207:16:31;;;6204:2;;;6241:6;6233;6226:22;6204:2;6269:66;6327:7;6316:8;6305:9;6301:24;6269:66;:::i;:::-;6259:76;;6354:64;6410:7;6405:2;6394:9;6390:18;6354:64;:::i;:::-;6344:74;;6471:3;6460:9;6456:19;6443:33;6427:49;;6501:2;6491:8;6488:16;6485:2;;;6522:6;6514;6507:22;6485:2;;6550:68;6610:7;6599:8;6588:9;6584:24;6550:68;:::i;:::-;6540:78;;;6665:3;6654:9;6650:19;6637:33;6627:43;;6689:64;6745:7;6739:3;6728:9;6724:19;6689:64;:::i;:::-;6679:74;;5604:1155;;;;;;;;;;;:::o;6764:489::-;;6858:5;6852:12;6885:6;6880:3;6873:19;6911:4;6940:2;6935:3;6931:12;6924:19;;6977:2;6970:5;6966:14;6998:3;7010:218;7024:6;7021:1;7018:13;7010:218;;;7089:13;;7104:42;7085:62;7073:75;;7168:12;;;;7203:15;;;;7046:1;7039:9;7010:218;;;-1:-1:-1;7244:3:31;;6828:425;-1:-1:-1;;;;;6828:425:31:o;7258:442::-;;7354:5;7348:12;7381:6;7376:3;7369:19;7407:4;7436:2;7431:3;7427:12;7420:19;;7473:2;7466:5;7462:14;7494:3;7506:169;7520:6;7517:1;7514:13;7506:169;;;7581:13;;7569:26;;7615:12;;;;7650:15;;;;7542:1;7535:9;7506:169;;7705:329;;7795:6;7790:3;7783:19;7847:6;7840:5;7833:4;7828:3;7824:14;7811:43;7899:3;7892:4;7883:6;7878:3;7874:16;7870:27;7863:40;8023:4;7953:66;7948:2;7940:6;7936:15;7932:88;7927:3;7923:98;7919:109;7912:116;;7773:261;;;;;:::o;8039:381::-;8120:42;8201:2;8193:5;8187:12;8183:21;8178:3;8171:34;8268:4;8261:5;8257:16;8251:23;8244:31;8237:39;8230:4;8225:3;8221:14;8214:63;8338:2;8330:4;8323:5;8319:16;8313:23;8309:32;8302:4;8297:3;8293:14;8286:56;;8405:4;8398:5;8394:16;8388:23;8381:31;8374:39;8367:4;8362:3;8358:14;8351:63;8100:320;;:::o;8425:264::-;;8602:2;8591:9;8584:21;8622:61;8679:2;8668:9;8664:18;8656:6;8622:61;:::i;8694:1227::-;8927:2;8979:21;;;9049:13;;8952:18;;;9071:22;;;8694:1227;;8927:2;9112;;9130:18;;;;9171:15;;;8694:1227;9217:678;9231:6;9228:1;9225:13;9217:678;;;9296:6;9290:13;9332:2;9326:9;9365:1;9361:2;9358:9;9348:2;;9371:9;9348:2;9395:15;;9449:11;;;9443:18;9484:42;9560:21;;;9546:12;;;9539:43;9622:11;;;9616:18;9602:12;;;9595:40;9658:4;9706:11;;;9700:18;9696:27;;9682:12;;;9675:49;9747:4;9795:11;;;9789:18;9785:27;9771:12;;;9764:49;9842:4;9833:14;;;;9870:15;;;;9253:1;9246:9;9217:678;;;-1:-1:-1;9912:3:31;;8907:1014;-1:-1:-1;;;;;;;8907:1014:31:o;9926:2898::-;;10499:3;10540:2;10529:9;10525:18;10573:1;10565:6;10562:13;10552:2;;10579:9;10552:2;10599:25;;;10643:2;10661:18;;;10654:30;;;;10719:22;;;10760:3;10779:18;;;;10839:15;;;10824:31;;10820:40;;;;10883:6;10907:4;10920:1523;10934:6;10931:1;10928:13;10920:1523;;;11023:66;11011:9;11003:6;10999:22;10995:95;10990:3;10983:108;11143:6;11130:20;11230:66;11221:6;11205:14;11201:27;11197:100;11177:18;11173:125;11163:2;;11315:4;11309;11302:18;11163:2;11348:31;;11434:19;;11419:35;;11504:14;;;11491:28;11474:15;;;11467:53;11543:4;11597:14;;;11584:28;11567:15;;;11560:53;11636:4;11690:14;;;11677:28;11660:15;;;11653:53;11402:4;11729;11787:14;;;11774:28;11859:14;11855:26;;;11883:66;11851:99;11825:126;;11815:2;;11968:4;11962;11955:18;11815:2;12003:32;;12062:21;;12110:18;12099:30;;12096:2;;;12145:4;12139;12132:18;12096:2;12199:6;12183:14;12179:27;12172:5;12168:39;12165:2;;;12223:4;12217;12210:18;12165:2;12267;12262;12254:6;12250:15;12243:27;12293:70;12359:2;12351:6;12347:15;12339:6;12334:2;12325:7;12321:16;12293:70;:::i;:::-;12421:12;;;;12283:80;-1:-1:-1;;;12386:15:31;;;;-1:-1:-1;;;10956:1:31;10949:9;10920:1523;;;10924:3;;;12493:9;12485:6;12481:22;12474:4;12463:9;12459:20;12452:52;12527:47;12567:6;12559;12527:47;:::i;:::-;12513:61;;;12583:65;12642:4;12631:9;12627:20;12619:6;12583:65;:::i;:::-;12697:9;12689:6;12685:22;12679:3;12668:9;12664:19;12657:51;12725:49;12767:6;12759;12725:49;:::i;:::-;12717:57;;;12811:6;12805:3;12794:9;12790:19;12783:35;10479:2345;;;;;;;;;;:::o;12829:341::-;13031:2;13013:21;;;13070:2;13050:18;;;13043:30;13109:19;13104:2;13089:18;;13082:47;13161:2;13146:18;;13003:167::o;13175:356::-;13377:2;13359:21;;;13396:18;;;13389:30;13455:34;13450:2;13435:18;;13428:62;13522:2;13507:18;;13349:182::o;13536:242::-;13606:2;13600:9;13636:17;;;13683:18;13668:34;;13704:22;;;13665:62;13662:2;;;13730:9;13662:2;13757;13750:22;13580:198;;-1:-1:-1;13580:198:31:o;13783:180::-;;13879:18;13871:6;13868:30;13865:2;;;13901:9;13865:2;-1:-1:-1;13952:4:31;13933:17;;;13929:28;;13855:108::o;13968:156::-;14056:42;14049:5;14045:54;14038:5;14035:65;14025:2;;14114:1;14111;14104:12;14025:2;14015:109;:::o
Swarm Source
ipfs://364a6941bea69620b7dc3a957d0ab4cbf3bfc459c7ad3924d220620aca9202fc
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.