Delegatecall | Solidity 0.8

Smart Contract Programmer
23 Dec 202109:25

Summary

TLDRThe video explains delegate call in Solidity, where a contract can execute code in another contract while retaining the context of the calling contract. It demonstrates an example where Contract A calls Contract B which delegate calls Contract C. Despite Contract C's code executing, message.sender remains Contract A and message.value remains what A sent since the context is preserved from B. The video then shows how delegate call allows upgrading a contract's logic by deploying a new version of the called contract. However, it warns that the called contract must keep identical state variable declarations to avoid issues.

Takeaways

  • 😀 With delegatecall, the called contract executes with the context of the calling contract. Message sender and msg.value are preserved.
  • 👍🏻 When contract B delegatecalls contract C, msg.sender in C is the original caller to B. This differs from a regular call.
  • 🔑 State variables are stored in the calling contract, not the one being delegatecalled. So contract C uses B's storage.
  • ✨ You can upgrade a contract's logic via delegatecall since it uses the calling contract's state.
  • 🚧 If you change state variable order when upgrading, it will break compatibility and cause issues.
  • 👀 delegatecall allows "logic separation" - state stored separately from logic.
  • ⚠️ Security risk: delegatecalled code can withdraw funds from the calling contract.
  • 📝 delegatecall returns a bool for success and bytes for any return data.
  • 💡 Can specify the function sig instead of a string when encoding for delegatecall.
  • 😊 Overall delegatecall allows you to efficiently reuse and upgrade code & logic in contracts.

Q & A

  • What is delegate call in Solidity?

    -Delegate call is a function in Solidity that allows a contract to execute code from another contract, while retaining the context of the calling contract. The called contract's code executes in the environment of the calling contract.

  • How does message.sender work with delegate call?

    -With delegate call, message.sender inside the called contract will be the original caller to the calling contract. This preserves the original message sender context.

  • How does message.value work with delegate call?

    -Message.value inside the called contract via delegate call will be the original amount sent to the calling contract that initiated the delegate call. The context including the amount sent is preserved.

  • Which contract's state variables are updated with delegate call?

    -The calling contract's state variables are updated when using delegate call. The called contract code executes using the calling contract's storage and state.

  • Can you upgrade contract logic using delegate call?

    -Yes, delegate call allows upgrading the logic executed by a contract even though the contract code itself is immutable. The calling contract delegates execution to updated logic contracts.

  • What are the risks of changing state variable order with delegate call upgrades?

    -If new state variables are added or order is changed in an upgrade, it can break the delegate call mapping to storage. Existing state must be preserved in original order.

  • Does ether sent from the called contract go to the calling contract?

    -Yes, any ether sent from the called contract is deducted from the calling contract's balance. The context of the calling contract is fully preserved.

  • Can new state variables be safely added to upgrade contracts?

    -New state variables can be safely added in upgrade contracts as long as they are appended after existing variables, preserving original order.

  • What data is returned from a delegate call function?

    -Like a regular call, delegate call returns a boolean indicating success and bytes data returned from the function call.

  • When would you use delegate call over a regular function call?

    -Delegate call is useful to leverage an existing contract's storage and context for execution. It saves deploying new contracts in some upgrade cases.

Outlines

00:00

😊 What is delegatecall and how it executes code in the context of the calling contract

This paragraph explains delegatecall which allows a contract to execute code from another contract while retaining the context of the calling contract. It illustrates this with an example where Contract A calls Contract B which then delegatecalls Contract C. When executing Contract C's code via delegatecall, msg.sender is preserved as Contract A rather than Contract B which called it. Similarly, msg.value in Contract C is the amount sent from A to B. The code in Contract C runs with the state variables of B. Overall, delegatecall allows code execution in one contract using the context of another.

05:03

👍 Using delegatecall to upgrade contract logic by delegating to a new contract instance

This paragraph demonstrates how delegatecall can be used to upgrade contract logic. A contract called DelegateCall is shown that delegatecalls TestDelegateCall. By redeploying TestDelegateCall but keeping DelegateCall the same, the logic executed from DelegateCall is updated even though DelegateCall's own code is immutable. Important best practices when using this pattern are called out - state variables must be kept identical between contract versions including order, otherwise storage collisions can cause issues.

Mindmap

Keywords

💡delegate call

A type of function call in Solidity where the code is executed within the context of the calling contract rather than the called contract. This allows the calling contract to update the logic of the called contract while retaining the same state variables.

💡message.sender

A global variable in Solidity that refers to the address of the account that initiated the transaction. With delegate call, message.sender will be the original caller rather than the intermediate contract making the delegate call.

💡message.value

A global variable holding the amount of ether sent with the transaction. With delegate call, message.value reflects the amount sent from the original caller rather than any intermediate contracts.

💡storage layout

How contract state variables are mapped or organized within a contract's storage. If this changes between versions of a contract, delegate call can lead to issues accessing state from the calling contract.

💡function signature

Uniquely identifies a function in Solidity code based on its name and parameter types. Used when encoding function calls for delegate call.

💡encode function

Prepare a function call for delegate call by encoding the function signature and parameters. Done using ABI encoding in Solidity.

💡selector

An alternate way to encode a function call that avoids specifying the function signature as a string. Simply references the function name and Solidity handles encoding.

💡storage

Where contract state variables and data are stored. Updated based on code execution even when using delegate call to another contract.

💡state variables

Variables declared at a contract level that store state on the blockchain. Must match perfectly between old and new contract versions when using delegate call.

💡update contract logic

A key benefit of delegate call - can upgrade logic of a contract without changing its state. Calling contract adopts new logic while retaining original state variables.

Highlights

Delegate call executes code in another contract in the context of the contract that called it

Message.sender will be the account that originally called the first contract

Message.value will be based on the amount sent from the first contract

Code inside the delegate called contract uses state variables of the calling contract

Delegate call preserves context - message.sender and message.value remain the same in the called contract

Can update delegate calling contract's logic even though its code can't change after deploying

Must keep same state variables and order when upgrading via delegate call

Changing state variable order messes up storage layout and causes issues

Add new state variables by appending, not inserting, to avoid problems

Encode function signature instead of function name string for delegate call

Delegate call returns success boolean and data like regular call function

Require success to check delegate call didn't fail

Called contract's state variables remain uninitialized after delegate call

Can update delegate called contract's logic to change functionality

Delegate call allows upgrading contract functionality without changing its code

Transcripts

play00:00

delegate call executes code in another

play00:02

contract in the context of the contract

play00:04

that called it let's take a look for

play00:06

example let's say that a this can be a

play00:08

contract or it can be an account so a

play00:11

calls b and then sends 100 way

play00:14

next b calls c and then sends 50 weight

play00:17

inside contract c message.sender will be

play00:20

equal to b

play00:21

message.value will be equal to 50. this

play00:24

is because b sent to c 50 way

play00:27

and whatever code that is inside

play00:29

contract c

play00:30

will be executed with the state

play00:32

variables inside contract c

play00:34

if there's a code inside contract c that

play00:36

queries the balance of the stored in

play00:38

contract c or it might send some leave

play00:41

to another contract then it's going to

play00:43

use the if stored in contract c this is

play00:46

a regular call so let's take a look how

play00:48

it's different when we use delegate call

play00:51

again a calls b and sends 100 way now

play00:54

unlike the first example this type b

play00:56

delegates call to c

play00:58

delegate call means that it's going to

play01:00

be executing the code inside the

play01:02

contract that is being called with the

play01:04

state variables and other context of the

play01:06

contract that called

play01:08

so when b delegates call to c

play01:11

message.sender will be a

play01:13

this is a little bit surprising since b

play01:15

called c so we expect message.sender to

play01:19

be equal to b

play01:20

but since we called delegate call

play01:22

here message.center will be a

play01:25

this is because when we query

play01:27

message.sender inside contract b

play01:29

it will be equal to a

play01:31

a called b

play01:32

and inside b message.sender will be

play01:35

equal to a

play01:36

delegate call preserves the context so

play01:39

when b delegates call to c

play01:42

message.sender remains it is still equal

play01:44

to a for the same reason

play01:47

message.value will be equal to 100.

play01:49

a sent 100 way to b

play01:52

so inside contract b message.value will

play01:54

be equal to 100 delegate call preserves

play01:57

the context so inside contract c

play01:59

message.value will still be equal to

play02:01

100. code inside contract c will be

play02:04

executed with the state variables of b

play02:06

and inside contract c if it sends ether

play02:09

to another contract or it queries the

play02:11

ether balance inside the contract

play02:13

then it's going to use the if stored in

play02:15

contract b

play02:17

for this example we'll call the function

play02:18

set bars

play02:20

and we'll delegate call to this contract

play02:22

test delegate call calling this function

play02:26

inside this function we will set a state

play02:28

variable num to the input num and then

play02:30

also record message.sender and amount of

play02:33

if that was sent message.value to use

play02:35

delegate call we type test dot

play02:39

delegate

play02:40

call

play02:41

and similar to call inside here we abi

play02:44

encode the function that we're going to

play02:45

be calling followed by the parameters

play02:47

that we're going to be passing the

play02:49

function that we're going to be calling

play02:50

is set bars so you'll say abi dot

play02:54

encode with

play02:57

signature

play02:59

the function that we're calling is set

play03:01

bars

play03:02

parentheses and the input is uint256

play03:07

and for the input we'll pass in the num

play03:09

now instead of typing a string to

play03:11

specify the function that we're calling

play03:14

there is another way so let me show you

play03:15

that

play03:16

so i'm going to comment this

play03:18

watch i'm going to copy this

play03:20

paste it here

play03:22

and replace this whole line with abi dot

play03:26

encode

play03:28

with

play03:29

selector

play03:30

parentheses

play03:32

and then we'll say

play03:34

test

play03:35

delegate call dot

play03:37

set

play03:38

bars dot

play03:40

selector

play03:41

so these two code will do exactly the

play03:43

same thing

play03:45

the benefit of writing it this way is

play03:47

that now you don't have to write a

play03:48

string so if you change the function

play03:50

signature of testdelegatecall.setbars

play03:53

then you don't have to make any change

play03:54

here whereas if you were to use a string

play03:57

then you will have to update the

play03:58

function signature of the string

play04:00

for this example we'll use this syntax

play04:03

but both syntax accomplish the same

play04:06

thing testificate call returns two

play04:08

outputs similar to call so it'll be

play04:10

boolean

play04:12

success and some kind of output data

play04:15

bytes

play04:16

memory

play04:17

data

play04:19

and lastly we'll check that the delegate

play04:20

call was successful by typing require

play04:24

success

play04:26

delegate call fail

play04:29

this should be abi encode with selector

play04:34

and that is how you use delegate call

play04:36

let's deploy this contract and also this

play04:39

contract and then we'll call the

play04:41

function set bars i'll hit ctrl s to

play04:43

compile the contract

play04:45

we'll deploy delegate call and test the

play04:48

date call

play04:49

scroll down and then open delegate call

play04:52

we'll call a function set bars and this

play04:54

will delegate call to test delegate call

play04:57

execute the code inside here

play04:59

but since we're delegating call to test

play05:02

delegate call

play05:04

the state variable that you see over

play05:05

here will not be updated

play05:07

instead it is the state variable of the

play05:10

contract that called in this case

play05:11

delegate call that will be updated i'll

play05:14

copy the address of test delegate call

play05:17

paste it here

play05:18

for the input to pass to the function

play05:20

set bars i'll pass in

play05:23

one two three

play05:24

and we'll also send some

play05:26

ether

play05:27

we'll send one one one

play05:29

way

play05:30

scroll down and then call set bars check

play05:32

the state variable num and it is equal

play05:34

to one two three the sender that is the

play05:37

account that you see over here

play05:40

and the amount of ether that we sent

play05:42

is 111. since we executed the code

play05:44

inside here using the state variable

play05:47

over here

play05:48

we expect to see that these state

play05:50

variables inside test delegate call to

play05:52

be uninitialized

play05:54

so i'll scroll down expand test delegate

play05:56

call and then call num

play05:58

and it is still equal to zero sender is

play06:01

zero address and the value is zero all

play06:03

of these state variables are

play06:05

uninitialized

play06:06

now using delegate call we can update

play06:09

this delegate call contract the state

play06:11

variables will be the same but the code

play06:14

that we're executing can be updated

play06:17

which means that we can update this

play06:18

contract even though once this contract

play06:20

is deployed we cannot change the code

play06:23

inside here for example let's change how

play06:26

the num state variable is set

play06:29

previously we just set it to the input

play06:32

this time i'll just double it

play06:34

and then set it to no

play06:36

we'll redeploy test delegate call

play06:39

but the contract delegate call will use

play06:41

the previous example so i'll compile the

play06:43

contract and then we'll redeploy test

play06:45

delegate call hit deploy

play06:49

scroll down

play06:50

and then expand on the contract delegate

play06:53

call

play06:54

clear the inputs

play06:56

copy the address of the new contract

play06:58

task delegate call this will double the

play07:01

input

play07:02

paste it in here

play07:04

and for the input we'll pass in 100

play07:07

and then call set bars

play07:09

get the new state variable and look it

play07:12

is equal to 200. what we just did here

play07:14

was we were able to update the logic the

play07:18

code of delegate call even though we

play07:20

cannot change any of the code inside

play07:22

this contract now it is important to

play07:24

remember that when you're using delegate

play07:26

call to update your contract logic

play07:29

then all of the state variables have to

play07:31

be the same in the exact same order so

play07:34

if you were to change the order of the

play07:36

state variables or maybe add a new state

play07:38

variable on top of the old one

play07:41

then you would see some weird stuff

play07:43

going on with your contract delegate

play07:45

call and to show you this i'll add an

play07:47

address say public and then owner

play07:51

we'll call set bars again and i'll show

play07:53

you that now when we call this function

play07:55

we'll get some weird results i'll

play07:58

compile the contract and we'll redeploy

play08:00

the new test delegate call contract

play08:02

scroll down

play08:04

clear the input

play08:06

copy the azure subtest delegate call

play08:09

paste it here and then pass in some

play08:11

input for no

play08:12

passing one two three again and then

play08:14

call set bars since we passed in one to

play08:16

three we expect num to be two times one

play08:19

to three which will be two four six

play08:22

however if we call the function num

play08:25

notice that it is still equal to 200.

play08:28

sender we expect it to be this account

play08:30

but if we call sender now we get

play08:32

something weird

play08:33

and we set zero for the value so we

play08:36

expect to be zero

play08:37

but we get some very large number the

play08:40

reason why you're seeing some weird

play08:41

results here is because when we added a

play08:43

new state variable on top of the old one

play08:46

we changed the storage layout so the

play08:48

code that is being executed here

play08:51

writes to a different storage from the

play08:53

storage that is defined over here

play08:56

how state variables mapped to the

play08:57

storage of the contract is an advanced

play08:59

topic that i've covered in another video

play09:02

search for a video about hack solidity

play09:04

accessing private data the point is when

play09:06

you're using delegate call to update

play09:08

your contract code make sure that the

play09:10

original state variables are declared in

play09:13

the same order so instead of declaring

play09:15

address public owner at the top

play09:18

if you preserve the original state

play09:20

variables and then appended new state

play09:22

variables then this would have worked

Rate This

5.0 / 5 (0 votes)

英語で要約が必要ですか?