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.
An important note: in order for the DELEGATECALL to function properly, the storage layouts of the caller and the callee contracts (D and E, respectively, in our example above) needs to be compatible. If it is not, the DELEGATECALL may lead to unexpected behavior and erros.
You can quickly test above in Solidity Browser.
(send money out of the contract to some different address)in the callcode? – Loi.Luu May 07 '16 at 08:32thisbe the same in the two contexts, similar tomsg.senderandmsg.value? – Travis Jacobs Sep 14 '17 at 13:01When 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.And in that case, which Ether balance is used, the balance of E or the Balance of D ? – user2284570 Jun 20 '18 at 19:08this.balancewould be D's balance. See code comment the value of "this" is D, when invoked by either D's callcodeSetN or C.foo(). – eth Jun 21 '18 at 19:47