 I wanted to provide some background to all of the different moving parts that truffle provides for debugging and decoding. And you've already seen quite a bit of the effort involved in decoding with mapping keys. So to kind of give an overview of it, there's a number of core components involved. And the one that is perhaps most recognizable is this truffle debug command. That's a command line debugger. You give it a transaction hash and it gives you a REPL where you can type step next, step over, et cetera. But this is powered by a library truffle debugger. And the truffle debugger library just takes like a Web 3 provider and a list of compilations and gives an interface, a JavaScript interface for doing that stepping. And so what truffle debug does is it just wraps this with a REPL interpreter and a bunch of display logic. But besides that, besides our core debugger functionality, we have to decode all this data. And so we have two packages for doing decoding. Firstly, we have truffle decoder, which is a new high-level package. You give it a contract instance or a project and it will let you see all of the state variables in that same level of detail that Harry showed with mapping keys like nested mappings, nested structs, arrays, et cetera. Truffle decoder gives an interface for getting a lossless representation of those. Now, both of these two packages are powered under the hood by a package called truffle codec, which handles all of that core decoding logic itself in a way that requires very few dependencies. Like the only dependencies that truffle codec requires are things like BN and other data manipulation packages. But it makes no network connections of its own. It's just this standalone piece that knows how solidity organizes its storage. As part of this package, we need a way to represent these decoded values in this lossless way. And so we have a submodule codec.format, which provides this lossless machine-readable representation of both values and types. So you can see if you're dealing with a Uint 256 or Uint 128. And then all of this stuff is used in several other places, but most notably, it's used in truffle test, which today decodes events using our decoding functionality. And as of tomorrow, when we do a release, you'll see it will report stack traces. So those are the moving parts. I can now switch over to show you a demo. I'll start with the demo of the debugger, just really to increase awareness, since we think it's really quite robust these days. And so I wanted to show off some features. So I rather than write some complicated solidity code myself, I decided to find a project. And so I tornado cache has some pretty complicated solidity code. So I decided to use that as an example. And you'll see I'm going to be debugging, they have a contract ERC 20 tornado. For those of you that are not familiar, tornado cache is a privacy mixer mixes tokens using Merkel proofs. And tornado is implemented as a base contract tornado, and then it has a derived instance for ERC 20, and then it's separate derived instance for F itself. We'll be debugging ERC 20 tornado now, because it's a little more interesting. So if we look at contracts tornado, we're going to be debugging the withdrawal function. The withdrawal function is defined in tornado. But we're going to be calling ERC 20 tornado. And I'll show you where this starts. What are we going to debug? Well, I put this and this is an experimental feature we have in test debugging, where you can take any truffle contract interaction. Before my change, the code looked like this. This is just a truffle task. It does a tornado dot withdrawal. And so for this demo, I'm just going to put a debug invocation here. And then we're going to do truffle test dash dash debug. Well, that's going to just make sure I haven't disconnected. Pardon my computer for being slow. It seems to slow down the screen shares. So this is starting up truffle test. It's doing a fresh compilation here to make sure it can get all the information it needs to actually run the debugger. This is just the tornadoes test. They're just running. So their tests print out those two addresses, and then it will proceed with the tests. And you'll see shortly. Oh, that's out of order. Okay, well, that's a preview of the next demo. But when we get to the withdrawal, you'll see that this will break and get into a debugger instance any day. Just a quick question. Hi, Victor here. Where you're adding a weight debug into that code. So this is just adding specifically the debug functionality onto tornado caches already existing truffle tests. That's correct. Yeah. So truffle gives you this. It's experimental. It doesn't work in all cases, but it gives you this global, this debug global, which wraps a truffle contract interaction. So this is like tornado is a truffle contract instance. And this is an existing line of code. And we just wrap it and truffle tests then hooks into this. It's like, Oh, you made a transaction. Here you go. Let's debug it. And if you don't put the debug flag on your truffle test command, it won't run this. It'll give you a warning saying you use the debug global, but you didn't pass the debug flag. Okay, thank you. And this acts as a pass through. So if you continue the tests after the debugger, you'll still get logs. The tests will still run. So anyway, here we're into the debugger now. So why don't I pull up that contract again, your C tornado, right? And we're going to go next. And immediately, the debugger knows that we're no longer in the same file, right? Withdraw is not defined by your seat, your C 20 tornado is defined by the base tornado. And so we have to recognize the jump from one source file to another and that's fine. So I'll just hit end to step next or and we find ourselves in another file. So the truffle debugger keeps track of all of this by knowing, thanks to the solidity source maps that they provide. And our own logic to figure out, you know, what are the base contracts? Because if you look at tornado, tornado itself is a derived, derived contract of a Merkle tree with history and a reentrancy guard. And it works just fine. So let's let's jump to a somewhere deep in this transaction. There's this verifier that is stored. Well, first, we're on this tornado contract, and we have to deal with this verifier. So what is that? Well, we don't know what it is. That's fine. Let's set a breakpoint. So let's look at the verifier contract. And there's a verify function somewhere in here that takes some input. I don't know any how any of this stuff works. It just looked complicated. So let's debug this. So what line is this? 192. So we'll set a breakpoint for verifier dot sole online 192. And let's just step, let's just continue to that point. Can you move up your window a little because the lower half is usually blocked by all? Yeah, sure. How's this? Thanks. Yep. And I think it's a little off the screen too. Cool. So we're in this verify function. But how do we get here? Well, we let's just look at the stack trace. So yeah, we're on verifier line 192 and we came from line 224 and from there, we came from 233. We can look at all the variables here. So you notice that proof was passed in as a memory struct. So what does that look like? Well, it has a bunch of fields and each of the fields have a bunch of fields. You can see that we decode those just fine. If we step to the next line, let's step over so we can get a value for a different memory struct. Let's see what that looks like. Well, it's a different struct. You can see this all decodes with no problem. So where are we? All right, we're still coming from there. So let's just step up out. Can I ask an annoying question? Yeah, please. Can you step back? No, you can't. The architecture supports it. It's just never been implemented. Okay. Well, it supports the loose word. Harry, don't correct me. So yeah, notice now that we are on 224. If we step out again, we're going to be on 233. If we step out again, we're going to get pretty close to the end of this transaction. We're on tornado sole 87. There's more I could show, but in the effort of time, I'll just show one more thing, which is that if you want to see a list of all the variables that are available in scope at a particular point in time, you can just type v and you can see all these. This includes any decoded mapping keys that we're aware of and pretty much decodes anything we can figure out. Call data variables, well, call data pointers, memory variables, etc. And all the various globals are provided as well. So that's pretty much it for the debugger portion of this demo. Does anyone have any questions before I move on to show off the core decoding functionality? I have one question. Let's say I have this tornado contract and it's breaking somewhere. Let's say you just muddled in the code and you broke something and you called this debugger. How would the debugger show you what is breaking or where it's breaking? You know, really high level question. Yeah. So as of last week's release, Truffle Debugger will print in red the stack trace when it gets to the end of the transaction saying this is why it failed. But you could step through yourself manually to kind of identify the place where it failed. But hopefully the stack traces will assist that. Like what happens now is the stack trace is captured when there's a revert. This is all thanks to Harry's recent work. When we hit a revert, we figure out what the stack trace was and we save that for the end so that we can report it. Does that help? And so I've been using Truffle here and there as a development platform for testing. And generally when something goes wrong, there's times where I feel, oh, something reverted and then I have to go through and manually try and figure it out. I'm not very good with debugging. This is something new, right? This is a new feature. Yeah. Yeah. So stack traces in the debugger itself were released last week. We're going to be putting this functionality into Truffle test in this week's release. Cool. Yeah. So stay tuned for that. I was tempted to show off that functionality, but I'll wait for the release. So as for the decoder, so I have some examples here. I have test decoder.test.js and this is pretty stripped down. It imports two packages, the decoder and the codec. It grabs the ERC20 tornado and then it grabs all related artifacts. What do we do? Well, we get the deployed instance. Now we construct a decoder and we just want to see what all the decoder variables are. Let's see what that looks like. A full test and I'll just explicitly say I want decoder.test.js and there they all are. Note that we know not only what the value of each of these variables is, but what the inherited, where, which inherited contract they come from, right? Like zeros is not defined in ERC20 tornado. It's defined inside tornado, inside Merkle tree with history. And you can get this information. And this is in a human readable format using our result inspector. But let me show you one more example, which is this decoder raw because this decoded information is represented in a machine readable format. And this is to address kind of a need that we've seen in the community where existing decoders, Solidity data or ABI decoders, they just convert to the native JavaScript types, like in the JavaScript ecosystem, if you use, you know, one of the existing decoders, you'll just get a string literal. And you don't know if it's a, if it's supposed to be a bytes array, if it's supposed to be static length bytes array, a dynamic length bytes array. And so we wanted to make sure that we could capture that information in our decoding so that we can control the presentation. And you can see what that looks like. So if you look at you know, the token state variable token is an address. So we know that we say, oh, it's token is a type address. And it's not payable. And here's the value. And if you want to does a, you know, as a string, you can get it like that. But we can also look at more complex types like verifier verifier is a contract. So how do we represent that? Well, we have a type class contract. And so what is it? Well, it's a known contract at this address. And we even know what the name of the contract is. And so you can see this information provides this lossless representation. So you can have this very clear window into what data is stored by your contract. But more, this package, trouble code, I can trouble decoder, they can be used not only to look at state variables, just for this demo, I just state variables, but we also use it to decode events. We can use it to decode return values, revert strings. And inside the debugger, we use it to decode values on the stack and memory storage, et cetera. We have a question, Mick. Oh, please. Alex, does the debugger support assembly blocks, e.g. displaying very variables from assembly blocks? I guess even the tornado code may have it for calling the pairing pre-compile. Harry, does the debugger? The debugger will step through assembly blocks. Yes, the decoder does not currently support displaying assembly variables. So I noticed that recently, I wasn't paying attention to this until recently. I noticed recently the format for in the AST for assembly stuff changed. So now that might actually be possible. But the old format, that wouldn't really have been possible. But yeah, that's something I could look into, yeah. Yeah, and then the next question would be, any plans for supporting assembly variables? Well, yeah, it was something I hadn't really thought about before. But yeah, that's totally something I could look into, absolutely.