DELEGATECALL
basically says that I'm a contract and I'm allowing (delegating) you to do whatever you want to my storage. DELEGATECALL
is a security risk for the sending contract which needs to trust that the receiving contract will treat the storage well.
DELEGATECALL
was a new opcode that was a bug fix for CALLCODE
which did not preserve msg.sender
and msg.value
. If Alice invokes Bob who does DELEGATECALL
to Charlie, the msg.sender
in the DELEGATECALL
is Alice (whereas if CALLCODE
was used the msg.sender
would be Bob).
Details
When D does CALL on E, the code runs in the context of E: the storage of E is used.
When D does CALLCODE on E, the code runs in the context of D. So imagine that the code of E is in D. Whenever the code writes to storage, it writes to the storage of account D, instead of E.
contract D {
uint public n;
address public sender;
function callSetN(address _e, uint _n) {
_e.call(bytes4(sha3("setN(uint256)")), _n); // E's storage is set, D is not modified
}
function callcodeSetN(address _e, uint _n) {
_e.callcode(bytes4(sha3("setN(uint256)")), _n); // D's storage is set, E is not modified
}
function delegatecallSetN(address _e, uint _n) {
_e.delegatecall(bytes4(sha3("setN(uint256)")), _n); // D's storage is set, E is not modified
}
}
contract E {
uint public n;
address public sender;
function setN(uint _n) {
n = _n;
sender = msg.sender;
// msg.sender is D if invoked by D's callcodeSetN. None of E's storage is updated
// msg.sender is C if invoked by C.foo(). None of E's storage is updated
// the value of "this" is D, when invoked by either D's callcodeSetN or C.foo()
}
}
contract C {
function foo(D _d, E _e, uint _n) {
_d.delegatecallSetN(_e, _n);
}
}
When D does CALLCODE on E, msg.sender
inside E is D as commented in the code above.
When an account C invokes D, and D does DELEGATECALL on E, msg.sender
inside E is C. That is, E has the same msg.sender
and msg.value
as D.
You can quickly test above in Solidity Browser.
The issue is that the response
variable is not a memory pointer, it's a stack variable. When you use it as the pointer for your out data in the delegatecall, you're actually just writing the data to the 0
memory location. This doesn't cause errors because that space is reserved for "scratch space", but it also never loads the result into your return variable.
What you want to do is
assembly {
let status := delegatecall(sub(gas,5000), dest, add(calldata, 0x20), mload(calldata), 0, 32)
response := mload(0)
jumpi(invalidJumpLabel, iszero(status))
}
Best Answer
No, means that if you use delegate call in your contract and the code at the target address contains a line with
selfdestruct(someaddress)
, this code will be executed in your contract context, effectively destroying your contract.This means you should be careful when using
delegatecall
and only use it in contracts that you wrote or that you know well.Hope this helps