Check out a free preview of the full A Tour of Web 3: Ethereum & Smart Contracts with Solidity course

The "Diamond Pattern Delegate Calls" Lesson is part of the full, A Tour of Web 3: Ethereum & Smart Contracts with Solidity course featured in this preview video. Here's what you'd learn in this lesson:

ThePrimeagen creates a delegate function that can call a method in another contract using the context of the current contract. This is similar to using the call() method in JavaScript. The data remains in the diamond contract and the external address does not change.


Transcript from the "Diamond Pattern Delegate Calls" Lesson

>> So let's update our drawing. So now you can imagine, all right, so we know this happens, but how does it happen? Well, I bet we could all guess that there's a fallback function on your diamond contract that probably has maybe a list of addresses and functions associated with them.

That when I get called, I find out what function's being called, I check inside of my little mapping right here, grab that thing out, and then I call it, right? So if we were to build that, that's what we do, right? Okay, so we're kind of understanding the diamond a little bit better, right?

This makes more sense. So let's keep on going, let's move into delegate calls, which sounds fantastic but it's actually not all that complicated. So, it's easiest to know about a delegate call by first showing you just a touch of JavaScript. JavaScript also has the exact same concept. So what I'm gonna do is I'm gonna create a class called foo.

I'm gonna have a single private member called bar which has a number. I'm gonna have a function called foo that simply console.logs out foo, bar, right? [SOUND] Nothing like a good old foo bar on a Sunday. This dot, get it now, anyways, const foo = new foo, boom.

Then we're gonna; call that, then we're gonna go, and I'm gonna call it with bar is 42. So what are we gonna get here? Well, the first time we're gonna call it, we're gonna get undefined, right? I haven't said anything. The second time we're gonna call it, we're gonna call this function.

But I'm gonna set what this variable is, correct? And this variable is gonna point to something called bar, which I've set on this object, correct? Correct, so if I were to go in here, re-execute this, we should see those two values, right? Awesome. Okay, so we all kinda know, effectively, how that works with JavaScript.

You've also seen bind and apply, right? Exact same concepts as these. All right, fantastic. So, let's do some more live coding. I put a little piece there just in case I forget how to do it. Let's go back to our fallback contract. And let's actually create something called A.sol, right?

All right, so I'm gonna to do the same pragma statement as always, holy cow. I'm gonna to import a hardhat/console.sol as always, and I'm gonna to create a contract A, and on A, I'm gonna have a uint a. I'm gonna have a function setA, which will take in a uint a.

It's gonna be public, and I'm gonna go a = a. I'm gonna have a function getA which will simply be a public view returns (uint), boom, boom, boom, right? We all know this. Erase that, return a. All right, none of this is confusing, right? We understand this at this point, I'm gonna take a, replace it with b, call it g, and have the exact same thing, another contract that looks identical to A, except this one's B.

But instead of just simply doing this whole set b thing, let's also go like this. Let's have a constructor that takes in the address of A. Then I'm gonna have an address of contractA. I don't wanna use the term capital A, because capital A, of course, is right here, obviously cause a couple problems.

Let's not do this. And so then I'm gonna go contractA = _A. And then I'm gonna go A, since I already have reference to the contract, I don't need to create an interface, though I could create an interface called IA that has these methods on that, but I'm not gonna do that.

So I'm just gonna go like this, contractA, then I pass in my address, and I'm gonna cast my address as a contract called A. And then I can simply go getA or setA and I can pass an a + 1. This looks pretty straightforward, right? I think we all kind of eventually get this concept.

Hopefully, I don't have this wrong cuz I will have to remember how to do it correctly. There we go. So this happens, right? We've now setA as a + 1. So let's go back to our deploy script. And even though we really liked class foo, let's get rid of that.

And go in here and let's take out this thing and go name and args, which is gonna be that, CI this and go, what is it, [INAUDIBLE] name, we'll pass in the name and then we'll pass in the args. So there we go. I'm gonna deploy a named contract.

And then I can also pass an arc to be passed to the constructor at deploy time. This makes sense. That's how you pass an arcs to a constructor. Alright, then I'll log the address and return it back out. Here, I'm just gonna do this, I'm gonna go const A = deploy("A"), right?

Then I'm gonna go constant b is deploy, what's this, B with of course A's address. This all makes sense, right? So then I'm gonna go console.log("A", a.getA()), perfect. Then I'm gonna go A, B, there we go, and B. So, if I've done these things correctly, we should just simply see A and B.

Wait, why are you doing this to us? Why are you doing this to me. All right, there we go. [SOUND] So I'm gonna just go fallback, even though that's not a really good name for this, There we go. This is beautiful, right? So we should see zero for the first two because we haven't assigned anything to A in either contract.

And then I'm gonna do await a.setA, start with 42. Then I'm gonna do it again, I probably should go console.log, just makes life easy having a nice little line right here, right? There we go, and let's go b.setA, how about 60? There we go. Now, what we should see is we should see 0, 0, then I would say we should see 42, 0, then we should see 61, 60, correct?

So let's run this. Hopefully, I haven't screwed anything up. I never used call, delegate call's kinda like the primary way. There we go. B is not a function, of course, it is not. Where did I do that? B, really just a genuine classic right there. So, it's exactly what we thought it would be, correct?

Yes, none of that should feel too surprising. So let's go back to contract B. And instead of doing this call, let's do a delegate call, as I showed you earlier. So I'm gonna do this. I'm gonna use just the raw address. I'm gonna go delegatecall, and then this is where things get a little bit interesting.

I'm gonna encode some information that needs to be sent to this address. So I'm gonna go abi.encodeWithSignature and go, setA with a uint 256. It's very important to correctly type it. If you say uint, it will not work. Spent a good half an hour figuring this out one day, very painful, much sadness, right?

And I'm gonna do a + 1. So there we go. We're gonna delegatecall out to a, with this function, with this signature, with this parameter, all right? So this should all be pretty much the exact same thing. We're gonna recall it, so hopefully what do we see? We should see what we expect, which is this.

Did everyone expect to see that? What happened at the bottom? We called A in perspective of B. Does that make sense? Just like when we did and then I passed it in an object that would represent bar, we're effectively doing the same type of call on a contract, except I don't wanna use my storage in your contract.

I want you to execute, but in my context. Now, are you ready for the mind blower? Are you ready? It's gonna hurt just a little bit. Honestly, it is gonna hurt just a little bit. And so I'm very sorry. Something like this. [LAUGH] A, we're gonna rename a to b, g, c.

Yes, obviously, I said no, no, no, no, no, yes, yes, yes, no, no, no, no, no, yes, yes, all right. And then of course this last one right here. Yes, there we go. So, I renamed the variable to b. What's gonna happen now, okay? I just named the variable different.

So, when I go to A and I execute, its variable's called A, mine's called B. Well, there's only one way to find out. I assume an error, right, that probably makes sense. Well, I hate to be the one to tell you this, nothing changed. So, does anyone wanna guess what's happening here.

I see mostly upset faces.
>> Did they both just need to pick one variable so it [INAUDIBLE]?
>> How about this, are you guys ready for this one? So I just swapped address with B and then I called setValue twice. Little surprise at what just happen there actually, I actually expected it to completely explodiate.

Did it not explodiate? I never actually tried this, I was just gonna try it live cuz I thought it'd be super duper awesome for it to just explode. But apparently it doesn't. I know why. Bool success, bytes memory bbb, they don't have an underscore. You can't use that, it's a reserved word.

Console.log("success"). Gosh, no even close, success, success. And I'm gonna do this one last time. So let's go here. You're gonna see this go through, [SOUND] there we go. Success, true. Success, true. Actually, I'm surprised by that. What is going on there? Anyways, we'll figure out what's going on there in just one second, actually, we will actually figure out what the heck's going on there.

So, I don't have a good explanation all of a sudden for why that's happening. It shouldn't happen that way. But we're gonna fix that. We're gonna make sure it does exactly what I think it's doing. Which I think I know what it's doing. We'll see. So, questions. I shouldn't done anything too live cuz sometimes I still get surprised by this.

I'm actually really surprised by that. And I'll show you why shortly why I'm super surprised about that. So let's improve our drawing. So now we know, if you had this little array, right, that has these functions and addresses. Not only can I call you, but I can call you in perspective of my data.

Now, does this make sense why all these contracts can share the same data and to be executed, and why we never need to change the address? In some sense, yes, this now is starting to make more sense. It has a list of fallback functions to address. I get a call in that has a function.

I look it up, get the address, get the function, send it over there, but execute it within my perspective of memory, we all share the same memory. Even though it exists, it has its own memory and never actually uses it. It always uses ours. So if we wanted to update the contract, what would happen?

We just add a new one of these, what would happen to our memory? Well, it's just staying here. What happens to all the outside addresses? It stays the same cuz this one does not change. We're effectively avoiding this whole problem of address change plus adding or removing functionality.

And that is the basis for the diamond. But there is one more problem that eventually always destroys people.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now