Skip to content

delegatecall #265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
geekberryonekey opened this issue May 8, 2024 · 10 comments
Closed

delegatecall #265

geekberryonekey opened this issue May 8, 2024 · 10 comments

Comments

@geekberryonekey
Copy link

  1. Is it possible to provide a version of aggregate that uses delegatecall,
  2. Is it possible to provide a method isContract to determine if an address is a contract by checking if the code size is greater than 0?
@mds1
Copy link
Owner

mds1 commented May 8, 2024

What is the use case for 1?

For 2, that is tracked in #146

@geekberryonekey
Copy link
Author

geekberryonekey commented May 9, 2024

1.1 For some contracts, they perform checks on the caller (msg.sender), such as supporting only whitelisted callers. In such cases, using call may result in incorrect readings, while using delegatecall can return the correct result.

1.2 For certain write methods, such as withdraw, they directly transfer funds to msg.sender. Using multicall will correctly transfer the amount to msg.sender, but using call will withdraw the amount to the multicall contract itself.

e.g: https://bscscan.com/tx/0x924970e981b924806c65b4e176d71c2f8f65e44cc62dec5ff1357b340eff70d7#eventlog

@mds1
Copy link
Owner

mds1 commented May 9, 2024

For those use cases, you want to delegatecall to the Multicall contract. This way your contract (say, a Safe wallet) executes some arbitrary sequence of calls and is the msg.sender during e.g. the whitelist check.

If the Multicall contract did the delegatecall, that means the whitelist check now enforces who can call the Multicall contract, because the whitelist code is executing in the context of Multicall

@geekberryonekey
Copy link
Author

geekberryonekey commented May 10, 2024

For those use cases, you want to delegatecall to the Multicall contract. This way your contract (say, a Safe wallet) executes some arbitrary sequence of calls and is the msg.sender during e.g. the whitelist check.

If the Multicall contract did the delegatecall, that means the whitelist check now enforces who can call the Multicall contract, because the whitelist code is executing in the context of Multicall

for example:

// here is fake Multicall code

contract Multicall {
  delegateCall({target,callData}) {
    target.delegatecall(callData)
 }
}

// here is fake Wallet code
contract Wallet {
    withdraw(value) {
        transfer(msg.sender, value)
   }
}

If called Wallet directly like this, transfer msg.sender should be userAddress

ether.call({
  from: userAddress,
  to: walletAddress,
  data: withdraw(value)
})

If called Wallet by delegate call like this, according to the DELEGATE_CALL by EIP-7 design, the transfer msg.sender should still be a userAddress, right?

ether.call({
  from: userAddress,
  to: multicallAddress,
  data: delegateCall({
    target: walletAddress,
    callData: withdraw(value)
    }) 
})

@mds1
Copy link
Owner

mds1 commented May 20, 2024

In your second example, where your EOA calls the multicall contract, and the multicall contract delegatecalls the wallet, then when withdraw executes the msg.sender is the EOA, and the multicall contract will transfer value ETH from itself to the EOA. In other words, the multicall contract is executing the wallet's logic in its own context—usually you don't want to execute within the multicall contract's context

I would suggest reading the solidity docs or some tutorials like the ones by @PatrickAlphaC to learn more about delegatecall and how it's typically used :)

@mds1 mds1 closed this as completed May 20, 2024
@marcelomorgado
Copy link

marcelomorgado commented May 24, 2024

Hi, I just saw this. I think there are use cases where a delegate call would help. For instance, Let's say I want to aggregate interaction with multiple DeFi protocols from a ERC-4337 smart account, and some of these calls are made through adapter/helper contracts.

call[0] = compound.deposit(1000 USDC) (call)
call[1] = compound.borrow(500 DAI) (call)
call[2] = anotherProtocolHelper.deposit(500 DAI) (delegate call)

It isn't possible right now. A solution may be having an isDelagatecall flag in the Call struct.

@mds1
Copy link
Owner

mds1 commented Jun 8, 2024

Sorry @marcelomorgado I don't understand. If you want to aggregate interaction from a 4337 smart account, you can already do that with batch calls. For example: https://github.com/eth-infinitism/account-abstraction/blob/f1c5c11b273b7ddae26bb20809419b33ccb8f043/contracts/samples/SimpleAccount.sol#L63-L82

Or if the wallet doesn't have native batching but does have delegatecall (like gnosis safe), you delegatecall from your wallet to the Multicall3 contract to execute batch calls.

@marcelomorgado
Copy link

Hi @mds1 let's say I want to have such call:

                                                                                                
                                                                                                
                                                                                                
                                                                                                
                                                                                                
                                                         ┌───────────────────┐                  
                                                         │                   │                  
                                                         │  Contract A       │                  
                                                         │                   │                  
                                                         └───────────────────┘                  
                                                               ▲                                
                                                               │                                
      ┌───────────────┐              ┌──────────────────┐      │       ┌───────────────────┐    
      │               │              │                  │ batch│       │                   │    
      │ Smart Wallet  ├─────────────►│ Multicall3       ├──────┴───────► Contract B        │    
      │               │ delegate call│                  │ delegate calls                   │    
      └───────────────┘              └──────────────────┘      │       ────────────────────┘    
                                                               │                                
                                                               │                                
                                                               ▼                                
                                                      ┌───────────────────┐                     
                                                      │                   │                     
                                                      │ Contract C        │                     
                                                      │                   │                     
                                                      └───────────────────┘                     
                                                                                                
                                                                                                
                                                                                                
                                                                                                

Since Multcall3 doesn't support batching delegate calls it isn't currently possible.
Note: I'm saying it on the context of KernelV3 wallet that doesn't support batch delegate calls.

@geekberryonekey
Copy link
Author

geekberryonekey commented Sep 24, 2024

Revise the point in requirement 2. I think the method name should not be called isContract, it should be named according to the underlying operation itself.
Here my example code:

contract CodeReader {
    function exeCodeSize(address addr) public view returns (uint256 size) {
        assembly {
            size := extcodesize(addr)
        }
    }

    function extCodeHash(address addr) public view returns (bytes32 codehash) {
        assembly {
            codehash := extcodehash(addr)
        }
    }

    function extCode(address addr) public view returns (bytes memory code) {
        uint256 size;
        assembly {
            size := extcodesize(addr)
        }
        if (size > 0) {
            code = new bytes(size);
            assembly {
                extcodecopy(addr, add(code, 0x20), 0, size)
            }
        }
    }
}

We also deployed some contract for us before Multicall update to v4.

network address
eth 0xc43e013be5b5dbebca5c65ec9aaf71786b98c95e
optimism 0xc43e013be5b5dbebca5c65ec9aaf71786b98c95e
bsc 0x2b7c3d71e052f047690125b72f64f3d8aac5cf10
etc 0x8f05f6ca1c6d72a2909e6e261e56db300819967a
polygon 0x76f66961135bb69a535a12d779d3ce6bd15bb7ea
base 0x279d943ad7c9acc437ba0493b71c4f356d6760e3
arbitrum 0x4fe31475333de3ae8dcd69668738a606c2e9bb13
avalanche 0x946a8b191370e4ede16a8b20cb7e2e3ae50992a6
eth sepolia 0x7d437ccd1ff4544501003480d24c06c44684b503
eth holesky 0x18720235fdcefff9d5152e54475a081e12ea081f
tron TKff5fznoKrufV5ZTm3zX1Ag7ah7Jrx2Fx

And found some deployed contract:

eth: 0xe9bbcd277e2c029c8b5f3c744dad93c290ad01a9 File 9 of 27 : AddressUpgradeable.sol L40

bsc: 0xcb8b1a33b5569d3728b4f834d01c7df0608fb184 L319

@yihuang
Copy link

yihuang commented May 11, 2025

It seems https://github.com/jaydenwindle/multicall-authenticated and ERC-2771 is the solution for point 1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants