Created
April 17, 2018 19:48
-
-
Save mikec/3ffbab8036b4913e66909d2b6eca7a74 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.19; | |
/** | |
* @title Proxy | |
* @dev Gives the possibility to delegate any call to a foreign implementation. | |
*/ | |
contract Proxy { | |
/** | |
* @dev Tells the address of the implementation where every call will be delegated. | |
* @return address of the implementation to which it will be delegated | |
*/ | |
function implementation() public view returns (address); | |
/** | |
* @dev Fallback function allowing to perform a delegatecall to the given implementation. | |
* This function will return whatever the implementation call returns | |
*/ | |
function () public payable { | |
address _impl = implementation(); | |
require(_impl != address(0)); | |
bytes memory data = msg.data; | |
assembly { | |
/* | |
delegatecall params explained: | |
gas: the amount of gas to provide for the call. `gas` is an Opcode that gives | |
us the amount of gas still available to execution | |
_impl: address of the contract to delegate to | |
add(data, 0x20): memory pointer to the start of `bytes memory data`. The 0x20 (32 bytes) | |
needs to be added because the first 32 bytes of the dynamic `bytes` type | |
contains the size of the data. This skips the first 32 bytes slot, and | |
points to the start of the actual data. | |
This param is the pointer to the `in` data. delegatecall will pass along data | |
from here, plus the `insize` (next param) | |
mload(data): loads the size of `bytes memory data`, which is in the first 32 byte slot. | |
mload takes a pointer (data is a pointer to the beginning of data) and reads | |
the 32 bytes of memory after the pointer. | |
This param is the `insize`, or the amount of data from `in` that delegate call | |
will pass along. | |
0, 0: These are for the `out` and `outsize` params. Because the output could be dynamic, | |
these are set to 0, 0 so the output data will not be written to memory. The output | |
data will be read using `returndatasize` and `returdatacopy` instead. | |
result: This will be 0 if the call fails and 1 if it succeeds | |
*/ | |
let result := delegatecall(gas, _impl, add(data, 0x20), mload(data), 0, 0) | |
/* | |
`returndatasize` is an Opcode that gives us the size of the last return data. In this case, that is the size of the data returned from delegatecall | |
*/ | |
let size := returndatasize | |
/* | |
0x40 is the "free memory slot", meaning a pointer to next slot of empty memory. mload(0x40) | |
loads the data in the free memory slot, so `ptr` is a pointer to the next slot of empty | |
memory. It's needed because we're going to write the return data of delegatecall to the | |
free memory slot. | |
*/ | |
let ptr := mload(0x40) | |
/* | |
`returndatacopy` is an Opcode that copies the last return data to a slot. `ptr` is the | |
slot it will copy to, 0 means copy from the beginning of the return data, and size is | |
the amount of data to copy. | |
*/ | |
returndatacopy(ptr, 0, size) | |
/* | |
if `result` is 0, revert. | |
if `result` is 1, return `size` amount of data from `ptr`. This is the data that was | |
copied to `ptr` from the delegatecall return data | |
*/ | |
switch result | |
case 0 { revert(ptr, size) } | |
default { return(ptr, size) } | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
could you point out what needs to be added ?