Transaction Hash:
Block:
14031670 at Jan-18-2022 08:23:56 PM +UTC
Transaction Fee:
0.00393668439742788 ETH
$10.35
Gas Used:
35,070 Gas / 112.252192684 Gwei
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x3B385794...59c08bFfc |
0.010794902211755034 Eth
Nonce: 26
|
0.006858217814327154 Eth
Nonce: 27
| 0.00393668439742788 | ||
0xEA674fdD...16B898ec8
Miner
| (Ethermine) | 1,171.771161653138866819 Eth | 1,171.771281241838866819 Eth | 0.0001195887 |
Execution Trace
AggregationRouter.swap( caller=0xd12bcdFB9A39BE79DA3bDF02557EFdcD5CA59e77, desc=[{name:srcToken, type:address, order:1, indexed:false, value:0xdd974D5C2e2928deA5F71b9825b8b646686BD200, valueString:0xdd974D5C2e2928deA5F71b9825b8b646686BD200}, {name:dstToken, type:address, order:2, indexed:false, value:0xdeFA4e8a7bcBA345F687a2f1456F5Edd9CE97202, valueString:0xdeFA4e8a7bcBA345F687a2f1456F5Edd9CE97202}, {name:srcReceivers, type:address[], order:3, indexed:false, value:[], valueString:[]}, {name:srcAmounts, type:uint256[], order:4, indexed:false, value:[], valueString:[]}, {name:dstReceiver, type:address, order:5, indexed:false, value:0x3B385794F894612D2287354e803993C59c08bFfc, valueString:0x3B385794F894612D2287354e803993C59c08bFfc}, {name:amount, type:uint256, order:6, indexed:false, value:32822229857267213892, valueString:32822229857267213892}, {name:minReturnAmount, type:uint256, order:7, indexed:false, value:31905213297284983116, valueString:31905213297284983116}, {name:flags, type:uint256, order:8, indexed:false, value:32, valueString:32}, {name:permit, type:bytes, order:9, indexed:false, value:0x, valueString:0x}], data=0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000000000000000000000E000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000061E7275F00000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000E294B87474C9ED8AECECC014F6032888FD0F49800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000001C77FFC27D2A1E6440000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000C0000000000000000000000000E294B87474C9ED8AECECC014F6032888FD0F4980000000000000000000000000DD974D5C2E2928DEA5F71B9825B8B646686BD200000000000000000000000000DEFA4E8A7BCBA345F687A2F1456F5EDD9CE972020000000000000000000000003B385794F894612D2287354E803993C59C08BFFC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 )
-
AdminUpgradeabilityProxy.70a08231( )
swap[AggregationRouter (ln:393)]
swapSimpleMode[AggregationRouter (ln:404)]
_isETH[AggregationRouter (ln:512)]
_permit[AggregationRouter (ln:514)]
safeTransferFrom[AggregationRouter (ln:524)]
call[TransferHelper (ln:94)]
encodeWithSelector[TransferHelper (ln:94)]
decode[TransferHelper (ln:96)]
_getBalance[AggregationRouter (ln:536)]
_isETH[AggregationRouter (ln:635)]
balanceOf[AggregationRouter (ln:638)]
_swapMultiSequencesWithSimpleMode[AggregationRouter (ln:538)]
decode[AggregationRouter (ln:578)]
safeTransferFrom[AggregationRouter (ln:588)]
call[TransferHelper (ln:94)]
encodeWithSelector[TransferHelper (ln:94)]
decode[TransferHelper (ln:96)]
call[AggregationRouter (ln:600)]
encodeWithSelector[AggregationRouter (ln:601)]
revert[AggregationRouter (ln:607)]
call[AggregationRouter (ln:615)]
encodeWithSelector[AggregationRouter (ln:616)]
revert[AggregationRouter (ln:625)]
sub[AggregationRouter (ln:547)]
_getBalance[AggregationRouter (ln:547)]
_isETH[AggregationRouter (ln:635)]
balanceOf[AggregationRouter (ln:638)]
Swapped[AggregationRouter (ln:554)]
Exchange[AggregationRouter (ln:562)]
_isETH[AggregationRouter (ln:565)]
_isETH[AggregationRouter (ln:412)]
_isETH[AggregationRouter (ln:417)]
_isETH[AggregationRouter (ln:428)]
_permit[AggregationRouter (ln:429)]
safeTransferFrom[AggregationRouter (ln:431)]
call[TransferHelper (ln:94)]
encodeWithSelector[TransferHelper (ln:94)]
decode[TransferHelper (ln:96)]
_isETH[AggregationRouter (ln:440)]
safeTransferETH[AggregationRouter (ln:443)]
_getBalance[AggregationRouter (ln:453)]
_isETH[AggregationRouter (ln:635)]
balanceOf[AggregationRouter (ln:638)]
_getBalance[AggregationRouter (ln:454)]
_isETH[AggregationRouter (ln:635)]
balanceOf[AggregationRouter (ln:638)]
_getBalance[AggregationRouter (ln:459)]
_isETH[AggregationRouter (ln:635)]
balanceOf[AggregationRouter (ln:638)]
call[AggregationRouter (ln:462)]
encodeWithSelector[AggregationRouter (ln:463)]
revert[AggregationRouter (ln:466)]
sub[AggregationRouter (ln:471)]
_getBalance[AggregationRouter (ln:471)]
_isETH[AggregationRouter (ln:635)]
balanceOf[AggregationRouter (ln:638)]
sub[AggregationRouter (ln:474)]
add[AggregationRouter (ln:474)]
_getBalance[AggregationRouter (ln:475)]
_isETH[AggregationRouter (ln:635)]
balanceOf[AggregationRouter (ln:638)]
mul[AggregationRouter (ln:478)]
mul[AggregationRouter (ln:479)]
Swapped[AggregationRouter (ln:489)]
Exchange[AggregationRouter (ln:497)]
_isETH[AggregationRouter (ln:500)]
File 1 of 2: AggregationRouter
File 2 of 2: AdminUpgradeabilityProxy
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6; pragma abicoder v2; interface IERC20 { event Approval( address indexed owner, address indexed spender, uint256 value ); event Transfer(address indexed from, address indexed to, uint256 value); function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address owner) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 value) external returns (bool); function transfer(address to, uint256 value) external returns (bool); function transferFrom( address from, address to, uint256 value ) external returns (bool); } pragma solidity >=0.6.12; interface IAggregationExecutor { function callBytes(bytes calldata data) external payable; // 0xd9c45357 // callbytes per swap sequence function swapSingleSequence( bytes calldata data ) external; function finalTransactionProcessing( address tokenIn, address tokenOut, address to, bytes calldata destTokenFeeData ) external; } // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false library TransferHelper { function safeApprove( address token, address to, uint256 value ) internal { // bytes4(keccak256(bytes('approve(address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); require( success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: APPROVE_FAILED" ); } function safeTransfer( address token, address to, uint256 value ) internal { // bytes4(keccak256(bytes('transfer(address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); require( success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FAILED" ); } function safeTransferFrom( address token, address from, address to, uint256 value ) internal { // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); require( success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FROM_FAILED" ); } function safeTransferETH(address to, uint256 value) internal { (bool success, ) = to.call{value: value}(new bytes(0)); require(success, "TransferHelper: ETH_TRANSFER_FAILED"); } } // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math) library SafeMath { function add(uint256 x, uint256 y) internal pure returns (uint256 z) { require((z = x + y) >= x, "ds-math-add-overflow"); } function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { require((z = x - y) <= x, "ds-math-sub-underflow"); } function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow"); } function div(uint256 a, uint256 b) internal pure returns (uint256 c) { require(b > 0, "ds-math-division-by-zero"); c = a / b; } } interface IERC20Permit { function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; } library RevertReasonParser { function parse(bytes memory data, string memory prefix) internal pure returns (string memory) { // https://solidity.readthedocs.io/en/latest/control-structures.html#revert // We assume that revert reason is abi-encoded as Error(string) // 68 = 4-byte selector 0x08c379a0 + 32 bytes offset + 32 bytes length if ( data.length >= 68 && data[0] == "\x08" && data[1] == "\xc3" && data[2] == "\x79" && data[3] == "\xa0" ) { string memory reason; // solhint-disable no-inline-assembly assembly { // 68 = 32 bytes data length + 4-byte selector + 32 bytes offset reason := add(data, 68) } /* revert reason is padded up to 32 bytes with ABI encoder: Error(string) also sometimes there is extra 32 bytes of zeros padded in the end: https://github.com/ethereum/solidity/issues/10170 because of that we can't check for equality and instead check that string length + extra 68 bytes is less than overall data length */ require( data.length >= 68 + bytes(reason).length, "Invalid revert reason" ); return string(abi.encodePacked(prefix, "Error(", reason, ")")); } // 36 = 4-byte selector 0x4e487b71 + 32 bytes integer else if ( data.length == 36 && data[0] == "\x4e" && data[1] == "\x48" && data[2] == "\x7b" && data[3] == "\x71" ) { uint256 code; // solhint-disable no-inline-assembly assembly { // 36 = 32 bytes data length + 4-byte selector code := mload(add(data, 36)) } return string(abi.encodePacked(prefix, "Panic(", _toHex(code), ")")); } return string(abi.encodePacked(prefix, "Unknown(", _toHex(data), ")")); } function _toHex(uint256 value) private pure returns (string memory) { return _toHex(abi.encodePacked(value)); } function _toHex(bytes memory data) private pure returns (string memory) { bytes16 alphabet = 0x30313233343536373839616263646566; bytes memory str = new bytes(2 + data.length * 2); str[0] = "0"; str[1] = "x"; for (uint256 i = 0; i < data.length; i++) { str[2 * i + 2] = alphabet[uint8(data[i] >> 4)]; str[2 * i + 3] = alphabet[uint8(data[i] & 0x0f)]; } return string(str); } } contract Permitable { event Error(string reason); function _permit( IERC20 token, uint256 amount, bytes calldata permit ) internal { if (permit.length == 32 * 7) { // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory result) = address(token).call( abi.encodePacked(IERC20Permit.permit.selector, permit) ); if (!success) { string memory reason = RevertReasonParser.parse(result, "Permit call failed: "); if (token.allowance(msg.sender, address(this)) < amount) { revert(reason); } else { emit Error(reason); } } } } } /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() internal { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require( newOwner != address(0), "Ownable: new owner is the zero address" ); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } contract AggregationRouter is Permitable, Ownable { using SafeMath for uint256; address public immutable WETH; address private constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); uint256 private constant _PARTIAL_FILL = 0x01; uint256 private constant _REQUIRES_EXTRA_ETH = 0x02; uint256 private constant _SHOULD_CLAIM = 0x04; uint256 private constant _BURN_FROM_MSG_SENDER = 0x08; uint256 private constant _BURN_FROM_TX_ORIGIN = 0x10; uint256 private constant _SIMPLE_SWAP = 0x20; struct SwapDescription { IERC20 srcToken; IERC20 dstToken; address[] srcReceivers; uint[] srcAmounts; address dstReceiver; uint256 amount; uint256 minReturnAmount; uint256 flags; bytes permit; } struct SimpleSwapData { address[] firstPools; uint256[] firstSwapAmounts; bytes[] swapDatas; uint256 deadline; bytes destTokenFeeData; } event Swapped( address sender, IERC20 srcToken, IERC20 dstToken, address dstReceiver, uint256 spentAmount, uint256 returnAmount ); event Exchange(address pair, uint256 amountOut, address output); constructor(address _WETH) public { WETH = _WETH; } receive() external payable { assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract } function rescueFunds(address token, uint256 amount) external onlyOwner { if (_isETH(IERC20(token))) { TransferHelper.safeTransferETH(msg.sender, amount); } else { TransferHelper.safeTransfer(token, msg.sender, amount); } } function swap( IAggregationExecutor caller, SwapDescription calldata desc, bytes calldata data ) external payable returns (uint256 returnAmount) { require(desc.minReturnAmount > 0, "Min return should not be 0"); require(data.length > 0, "data should be not zero"); uint256 flags = desc.flags; // simple mode swap if (flags & _SIMPLE_SWAP != 0) return swapSimpleMode(caller, desc, data); uint256 amount = desc.amount; IERC20 srcToken = desc.srcToken; IERC20 dstToken = desc.dstToken; if (flags & _REQUIRES_EXTRA_ETH != 0) { require( msg.value > (_isETH(srcToken) ? amount : 0), "Invalid msg.value" ); } else { require( msg.value == (_isETH(srcToken) ? amount : 0), "Invalid msg.value" ); } require( desc.srcReceivers.length == desc.srcAmounts.length, "Invalid lengths for receiving src tokens" ); if (flags & _SHOULD_CLAIM != 0) { require(!_isETH(srcToken), "Claim token is ETH"); _permit(srcToken, amount, desc.permit); for (uint i = 0; i < desc.srcReceivers.length; i++) { TransferHelper.safeTransferFrom( address(srcToken), msg.sender, desc.srcReceivers[i], desc.srcAmounts[i] ); } } if (_isETH(srcToken)) { // normally in case taking fee in srcToken and srcToken is the native token for (uint i = 0; i < desc.srcReceivers.length; i++) { TransferHelper.safeTransferETH( desc.srcReceivers[i], desc.srcAmounts[i] ); } } address dstReceiver = (desc.dstReceiver == address(0)) ? msg.sender : desc.dstReceiver; uint256 initialSrcBalance = (flags & _PARTIAL_FILL != 0) ? _getBalance(srcToken, msg.sender) : 0; uint256 initialDstBalance = _getBalance(dstToken, dstReceiver); { // solhint-disable-next-line avoid-low-level-calls // may take some native tokens for commission fee uint256 ethAmount = _getBalance(IERC20(ETH_ADDRESS), address(this)); if (ethAmount > msg.value) ethAmount = msg.value; (bool success, bytes memory result) = address(caller).call{value: ethAmount}( abi.encodeWithSelector(caller.callBytes.selector, data) ); if (!success) { revert(RevertReasonParser.parse(result, "callBytes failed: ")); } } uint256 spentAmount = amount; returnAmount = _getBalance(dstToken, dstReceiver).sub(initialDstBalance); if (flags & _PARTIAL_FILL != 0) { spentAmount = initialSrcBalance.add(amount).sub( _getBalance(srcToken, msg.sender) ); require( returnAmount.mul(amount) >= desc.minReturnAmount.mul(spentAmount), "Return amount is not enough" ); } else { require( returnAmount >= desc.minReturnAmount, "Return amount is not enough" ); } emit Swapped( msg.sender, srcToken, dstToken, dstReceiver, spentAmount, returnAmount ); emit Exchange( address(caller), returnAmount, _isETH(dstToken) ? WETH : address(dstToken) ); } function swapSimpleMode( IAggregationExecutor caller, SwapDescription calldata desc, bytes calldata data ) public returns (uint256 returnAmount) { uint256 amount = desc.amount; IERC20 srcToken = desc.srcToken; IERC20 dstToken = desc.dstToken; require(!_isETH(srcToken), "src is eth, should use normal swap"); _permit(srcToken, amount, desc.permit); uint256 totalSwapAmount = amount; if (desc.srcReceivers.length > 0) { // take fee in tokenIn require( desc.srcReceivers.length == 1 && desc.srcReceivers.length == desc.srcAmounts.length, "Wrong number of src receivers" ); TransferHelper.safeTransferFrom( address(srcToken), msg.sender, desc.srcReceivers[0], desc.srcAmounts[0] ); require(desc.srcAmounts[0] <= totalSwapAmount, "invalid fee amount in src token"); totalSwapAmount -= desc.srcAmounts[0]; } address dstReceiver = (desc.dstReceiver == address(0)) ? msg.sender : desc.dstReceiver; uint256 initialDstBalance = _getBalance(dstToken, dstReceiver); _swapMultiSequencesWithSimpleMode( caller, address(srcToken), totalSwapAmount, address(dstToken), dstReceiver, data ); returnAmount = _getBalance(dstToken, dstReceiver).sub(initialDstBalance); require( returnAmount >= desc.minReturnAmount, "Return amount is not enough" ); emit Swapped( msg.sender, srcToken, dstToken, dstReceiver, amount, returnAmount ); emit Exchange( address(caller), returnAmount, _isETH(dstToken) ? WETH : address(dstToken) ); } // Only use this mode if the first pool of each sequence can receive tokenIn directly into the pool function _swapMultiSequencesWithSimpleMode( IAggregationExecutor caller, address tokenIn, uint256 totalSwapAmount, address tokenOut, address dstReceiver, bytes calldata data ) internal { SimpleSwapData memory swapData = abi.decode(data, (SimpleSwapData)); require(swapData.deadline >= block.timestamp, "ROUTER: Expired"); require( swapData.firstPools.length == swapData.firstSwapAmounts.length && swapData.firstPools.length == swapData.swapDatas.length, "invalid swap data length" ); uint256 numberSeq = swapData.firstPools.length; for (uint256 i = 0; i < numberSeq; i++) { // collect amount to the first pool TransferHelper.safeTransferFrom( tokenIn, msg.sender, swapData.firstPools[i], swapData.firstSwapAmounts[i] ); require(swapData.firstSwapAmounts[i] <= totalSwapAmount, "invalid swap amount"); totalSwapAmount -= swapData.firstSwapAmounts[i]; { // solhint-disable-next-line avoid-low-level-calls // may take some native tokens for commission fee (bool success, bytes memory result) = address(caller).call( abi.encodeWithSelector( caller.swapSingleSequence.selector, swapData.swapDatas[i] ) ); if (!success) { revert(RevertReasonParser.parse(result, "swapSingleSequence failed: ")); } } } { // solhint-disable-next-line avoid-low-level-calls // may take some native tokens for commission fee (bool success, bytes memory result) = address(caller).call( abi.encodeWithSelector( caller.finalTransactionProcessing.selector, tokenIn, tokenOut, dstReceiver, swapData.destTokenFeeData ) ); if (!success) { revert(RevertReasonParser.parse(result, "finalTransactionProcessing failed: ")); } } } function _getBalance(IERC20 token, address account) internal view returns (uint256) { if (_isETH(token)) { return account.balance; } else { return token.balanceOf(account); } } function _isETH(IERC20 token) internal pure returns (bool) { return (address(token) == ETH_ADDRESS); } }
File 2 of 2: AdminUpgradeabilityProxy
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import './UpgradeabilityProxy.sol'; /** * @title AdminUpgradeabilityProxy * @dev This contract combines an upgradeability proxy with an authorization * mechanism for administrative tasks. * All external functions in this contract must be guarded by the * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity * feature proposal that would enable this to be done automatically. */ contract AdminUpgradeabilityProxy is UpgradeabilityProxy { /** * Contract constructor. * @param _logic address of the initial implementation. * @param _admin Address of the proxy administrator. * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. */ constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable { assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)); _setAdmin(_admin); } /** * @dev Emitted when the administration has been transferred. * @param previousAdmin Address of the previous admin. * @param newAdmin Address of the new admin. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Modifier to check whether the `msg.sender` is the admin. * If it is, it will run the function. Otherwise, it will delegate the call * to the implementation. */ modifier ifAdmin() { if (msg.sender == _admin()) { _; } else { _fallback(); } } /** * @return The address of the proxy admin. */ function admin() external ifAdmin returns (address) { return _admin(); } /** * @return The address of the implementation. */ function implementation() external ifAdmin returns (address) { return _implementation(); } /** * @dev Changes the admin of the proxy. * Only the current admin can call this function. * @param newAdmin Address to transfer proxy administration to. */ function changeAdmin(address newAdmin) external ifAdmin { require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); emit AdminChanged(_admin(), newAdmin); _setAdmin(newAdmin); } /** * @dev Upgrade the backing implementation of the proxy. * Only the admin can call this function. * @param newImplementation Address of the new implementation. */ function upgradeTo(address newImplementation) external ifAdmin { _upgradeTo(newImplementation); } /** * @dev Upgrade the backing implementation of the proxy and call a function * on the new implementation. * This is useful to initialize the proxied contract. * @param newImplementation Address of the new implementation. * @param data Data to send as msg.data in the low level call. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. */ function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin { _upgradeTo(newImplementation); (bool success,) = newImplementation.delegatecall(data); require(success); } /** * @return adm The admin slot. */ function _admin() internal view returns (address adm) { bytes32 slot = ADMIN_SLOT; assembly { adm := sload(slot) } } /** * @dev Sets the address of the proxy admin. * @param newAdmin Address of the new proxy admin. */ function _setAdmin(address newAdmin) internal { bytes32 slot = ADMIN_SLOT; assembly { sstore(slot, newAdmin) } } /** * @dev Only fall back when the sender is not the admin. */ function _willFallback() internal override virtual { require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin"); super._willFallback(); } } // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import './Proxy.sol'; import '@openzeppelin/contracts/utils/Address.sol'; /** * @title UpgradeabilityProxy * @dev This contract implements a proxy that allows to change the * implementation address to which it will delegate. * Such a change is called an implementation upgrade. */ contract UpgradeabilityProxy is Proxy { /** * @dev Contract constructor. * @param _logic Address of the initial implementation. * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. */ constructor(address _logic, bytes memory _data) public payable { assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)); _setImplementation(_logic); if(_data.length > 0) { (bool success,) = _logic.delegatecall(_data); require(success); } } /** * @dev Emitted when the implementation is upgraded. * @param implementation Address of the new implementation. */ event Upgraded(address indexed implementation); /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Returns the current implementation. * @return impl Address of the current implementation */ function _implementation() internal override view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } /** * @dev Upgrades the proxy to a new implementation. * @param newImplementation Address of the new implementation. */ function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } /** * @dev Sets the implementation address of the proxy. * @param newImplementation Address of the new implementation. */ function _setImplementation(address newImplementation) internal { require(Address.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address"); bytes32 slot = IMPLEMENTATION_SLOT; assembly { sstore(slot, newImplementation) } } } // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @title Proxy * @dev Implements delegation of calls to other contracts, with proper * forwarding of return values and bubbling of failures. * It defines a fallback function that delegates all calls to the address * returned by the abstract _implementation() internal function. */ abstract contract Proxy { /** * @dev Fallback function. * Implemented entirely in `_fallback`. */ fallback () payable external { _fallback(); } /** * @dev Receive function. * Implemented entirely in `_fallback`. */ receive () payable external { _fallback(); } /** * @return The Address of the implementation. */ function _implementation() internal virtual view returns (address); /** * @dev Delegates execution to an implementation contract. * This is a low level function that doesn't return to its internal call site. * It will return to the external caller whatever the implementation returns. * @param implementation Address to delegate. */ function _delegate(address implementation) internal { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } /** * @dev Function that is run as the first thing in the fallback function. * Can be redefined in derived contracts to add functionality. * Redefinitions must call super._willFallback(). */ function _willFallback() internal virtual { } /** * @dev fallback implementation. * Extracted to enable manual triggering. */ function _fallback() internal { _willFallback(); _delegate(_implementation()); } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }