I made a delegatecall to the REP contract to approve my contract to spend tokens on behalf of a user and my transaction is failing for some reason here is my code:

(bool b,) = REPAddress.delegatecall(abi.encodeWithSignature("approve(address, uint256)", this, noShowBondPayment));
require(b, "delegatecall failed");

Am I using delegatecall correctly?

  • "approve my contract to spend tokens on behalf of a user" if you delegatecall from some contract into an ERC20 to call approve, it'll update the approval status in storage of the contract delegatecall'ing. This wont update any state in the REP contract and wont allow you to transfer REP from the caller. Could you elaborate on what you're trying to do and what you're trying to accomplish? – flygoing Apr 21 '19 at 17:52
  • @flygoing I was under the impression that delegatecall preserved the original value of msg.sender, meaning a delegatecall would be like calling approve directly. I'm trying to approve my contract to pay for something with user funds by approving and paying in one transaction. So the user won't have to make multiple transactions – skrrrraaaa Apr 21 '19 at 18:09
  • 2
    You're correct that it preserves msg.value and msg.sender, but the main point of delegatecall is that it delegates code execution of the current call frame to whatever address it's calling. So any SSTORE/SLOAD opcodes, and even the ADDRESS opcode, operates on the contract that called DELEGATECALL. So if contract A delegatecall's to contract B, the code in contract B operates on the storage of contract A, not on contract B. – flygoing Apr 21 '19 at 18:15
  • delegatecalling into an ERC20 from a non ERC20 will likely cause failure since it's operating on a completely different storage layout. – flygoing Apr 21 '19 at 18:15
  • @flygoing gotcha, so the only way for me to transfer tokens on behalf of a user is to have them call approve? – skrrrraaaa Apr 21 '19 at 18:26

No, correct will be: approve(address,uint256), without spaces.

(bool b,) = REPAddress.delegatecall(abi.encodeWithSignature("approve(address,uint256)", this, noShowBondPayment));

abi.encodeWithSignature returns the first 4 bytes of the sha3 of the first element + arguments. Because of the space, you get the wrong sha3 and as a result, the wrong pointer.

Not the answer you're looking for? Browse other questions tagged or ask your own question.