Webinar: Building Bitcoin & Ethereum Apps on Hemi with LocalNet
Hemi developer Clayton walks us through building on the Hemi LocalNet.
This 20-minute webinar discusses building Bitcoin and Ethereum apps on Hemi with LocalNet. Clayton, a software engineer on the Hemi team, explains the concept of Hemi as a layer-two blockchain that enables interoperability between Ethereum and Bitcoin. He also introduces LocalNet, a tool that allows developers to run Hemi locally on their machines, eliminating the need to rely on testnets and providing more control over the testing environment. Clayton will show a simple example of sending Bitcoin data from L2 to L1 and reading it from a smart contract on L1 using LocalNet. Remember that LocalNet is not meant for production-ready code but serves as a demonstration of Hemi’s functionality and it’s a great way to try it out on your local machine.
Transcript
Erin (00:04.332)
All right. Welcome, everyone. Thank you all for joining us today for building Bitcoin and Ethereum apps on Hemi with LocalNet. My name is Errin, and I’ll be your moderator for this webinar. Before we dive in, I’d just like to go over a few housekeeping items so that way we can share a smooth and enjoyable experience for everyone. First, please keep your microphone muted to minimize background noise while our speaker is presenting. We do encourage you to use the chat feature to share your thoughts and ask questions. We will have a dedicated time for Q &A towards the end.
Please try to hold your questions until then. Please note that today’s session is being recorded and will be made available after the event. And with that, I’d like to introduce Clayton, our Software Engineer on the Core Engineering team, who will be leading the webinar.
Clayton (01:00)
Hi everyone and welcome. I’ll share my screen really quick, but before I do, again, my name is Clayton. I’m a core engineer on the Hemi engineering team. And that’s just a fancy word for I write the software that Hemi runs on. So yeah, let me share my screen really quick. We’ll go over a few things.
All right, so can everybody see my screen? Awesome. So as I’m sure a lot of you are aware, but what is Hemi? Hemi is a Layer Two blockchain that makes Ethereum and Bitcoin interoperability possible, safe, and fast. So what we mean by this is when you’re running a Hemi L2 node,
Your L2 node is actually connecting directly to the Bitcoin peer-to-peer network. And then we also extended the EVM. We call it the HVM, because Hemi starts with an H, and we add pre-compiles to that in the HVM that allow you to, on your L2 node, communicate directly with the Bitcoin blockchain. And when you’re part of the Bitcoin peer-to-peer network itself,
You’re not relying on any third party like oracle or trusted entity to do so. You own the chain. It is your own full Bitcoin node that you’ll be querying data off. So within your HVM pre-compiled, you have access to that data.
Clayton (02:24.11)
So it would take a very long time to go over all of Hemi. There are lot of daemons to run. There’s a lot that makes Hemi work. And then in addition to that, when you were working with testnets, such as Sepolia or Bitcoin testnet, there were other struggles too, right? So one is reliability of the network. Two is that even though the currency is free, it does take some effort to get a test currency.
Knowing this, we developed something called LocalNet for Hemi. It’s something that runs 100 percent on your machine. And the way it does this is in the real world, Hemi depends on obviously Bitcoin and Ethereum. And in LocalNet, we run that completely locally on your machine. So you’ll be running Bitcoin in reg test mode and Ethereum in dev mode. And you’ll have access to control these networks however you feel is right for your testing.
So this allows you to not worry about the behavior of testnets and not have to worry about getting currency because you can just generate that currency locally for yourself.
Clayton (03:30.742)
So, how does this work? So it’s pretty straightforward. We use Docker. So we use Docker compose to orchestrate all these daemons that run. So if you look in the Hemi network readme, you’ll actually see it’s one command to run the whole Hemi LocalNet net all via Docker compose. So today I’m going to do a very, very trivial example of sending, we’re going to grab some Bitcoin data from L2 from our pre-compiles on the HVM.
And we’re going to send that down to L1. And then we’re going to read from a smart contract on L1. So this would be Ethereum L1 on your local machine. And it’s important to note that this is not meant to be production -ready code. There are many things that, if you were to run something like this in production, you need to account for for security purposes. This is just meant to demonstrate some functionality of Hemi. So I’ve already written this example. We’ll post a link to it after the webinar. But I’ll be copying and pasting code from the example that I already wrote
Then I’ll be explaining it. That way, it goes a little faster that way. I’ll do my best to slow down and explain different parts. But just for time’s sake, that’s the way I’ll be operating here. So again, we’ll be sending a link to the full example afterwards. And there’s a remit on how to run it. We’ll also be able to answer any of your questions on Discord as well. So with that, let’s get started. So here I have the Hemi network repository checked out.
I have this example directory. I’m going to call it read balances. So within here, it’s an MVM package, an MVM project, I should say. So I’ve already set up the dependencies just for time’s sake. But like I said, what we’re going to try to do here is what we’re going to do is we’re going to grab a balance of a Bitcoin address from our Hemi L2 node at some L2 block. So we have an idea of when this occurred. And then we’ll just down to the L1, we’ll read from that smart contract.
So as you can probably guess, we’re gonna need to deploy contracts in both our local L1 and L2. So I’ll just start with writing the L2 contract and I’ll explain it really quick. And then I will grab the code that I already wrote and I will explain it really quick. So I’m gonna paste a lot here, but again, no worries. I will go through it. Don’t panic.
Clayton (05:58.284)
And it will also be available afterwards if you want to take a look yourself. So on our L2, here, we’re going to create this contract. It has two methods. One is going to be getting a Bitcoin balance. So you give this some sort of Bitcoin address and then it will call our pre-compile in the hVM to get the balance of that. It’ll return it. And then we’re also going to have this, and this is the function actually or the method we actually use.
So what we’re going to do is we’re going to give them some Bitcoin addresses. We’re going to actually send that data down to L1. And the way we do this is we use something called the cross-domain messenger. So again, if you’re not familiar, Hemi is a fork of Optimism. So we’re using some functionality to get out of them. And we’re essentially going to use a cross-domain messenger to send this Bitcoin data down to L1. So we’re going to need the L1 contracts addresses for this. So we’ll take the Bitcoin address.
We will get the balance here. Then we’ll send that address, the balance, and the block number we got it at. So this is the L2’s block number down to the L1. Just so we have a record of when we got that data. So on the L1, we’re going to try to match this interface right here. So for the L1, we’re expecting an L1 rebalance contract to be stored. And we’re going to call setBitcoinAddressBalance on that. So we will create a new file here.
Clayton (07:39.192)
So again, this is not production-ready code. This is merely an example. So here’s our set Bitcoin address balance. So we’ll be getting a Bitcoin address, a balance, and an L2 block height from that call we made down to the L1. We’re just going to store it in contract storage here. So then we’ll have another method here to actually get that data. So we’ll be returning the balance at that address as well as the last block, L2 block that it was updated at. Just we have a of that.
So again, just to review, we’ll call setBitcoinBalanceAddress or sendBitcoinAddressBalance to L1. We’ll get the address as balance. We’ll send it via the cross -domain messenger with the block number we got it at. And then we’ll store that on L1 here. And then we’ll be able to read it from L1 whenever we want. So we’re actually getting Bitcoin data from the peer-to-peer network on an Ethereum L1. And we’ll be able to do this locally with LocalNet. So, how do we do this? Great question. All right.
So I’m going to write a small JavaScript file here to actually perform this functionality. So again, like I said, I’ve written out a lot of this already. So I’ll be copying and pasting similar to the contracts. Again, this is all will be all available online in the link we post afterwards. But let’s get started really quick. So like most programs, we’ll need a main function to perform our functionality. So I know all of this kind of stuff.
But off the top of my head from writing LocalNet But these are the kind of configs we’ll need here. So I already know that this is the Sol-C version. We’ll need to compile our smart contracts. I’m also going to paste a bit here. No panic, I’ll go through it. So on LocalNet, we expose the L1’s RPC to port 8545 near local machine. On L2, it’s 8546. So this is the Bitcoin address of our regtest miner.
So this one should have plenty of Bitcoin that we can actually send that value to our L1. And this right here is interesting. This is called the L1 standard bridge proxy address. So this is an address of a smart contract that allows you to bridge ETH to become HEMI ETH. So what this means is when we deploy contracts on L2, we are going to need some HEMI ETH to deploy those contracts. So we need to bridge ETH from L1 to our L2.
Clayton (10:04.302)
It will be using the L1 standard bridge proxy to do that. And it will become the same address as your L1 address. So down here, I tried to do this all without using Optimism’s SDK, but there was a little bit dug into later that I couldn’t quite get out with small enough code. So here I have two providers just from Web3 to connect to L1 and L2.
And then I have two providers using ethers that the Optimism SDK will use to communicate. And here’s a few other addresses, contract addresses that are set to LocalNet and a few other config values that are LocalNet net values here. Again, we’ll be posting online afterwards. Don’t worry if it seems like it’s going fast. So after we’ve had this setup, we can now
We’re going to want to bridge our ETH to become HEMI ETHs to allow us to deploy aaL2. So I’ll paste that code really quick and I’ll explain it.
So yeah, so first we’re going to want to get our dev account. So this will be the account. Since we’re in eth and dev mode, this is the account that gets all the eth generated to it. We’ll just print out the address for debugging purposes. And then we will order the account for debugging purposes. We’ll get some feed data. We’ll send our eth to the L1 standard bridge proxy. We’ll just give ourselves a bunch. And yeah, we’ll the feed data to get feeds.
Then we’ll send that transaction. So sending this to the L1 standard bridge proxy will then bridge our L1 to L2. Then we’ll log out. We’re done with bridging. Cool. And then now what we’re going to do is we’re going to want to deploy the L2 contract. So I’ll do that. I’ll paste that code right quick right here. A little poorly named, but we’ll unlock our account on L2 with our super-secret LocalNet net password. Again, this is all just on my local machine. I took a. Yeah, it’s okay if you all know the password. So anyway, this will be our L2 contract. We’ll deploy it to our local L2 with the dev account using the, and I’ll explain this function in a minute, using the L2 rebalances smart contract. It’s the file name, that’s the contract name.
We’re going to need a helper that I wrote to deploy the contract. Just to abstract this, it’s kind of a bit of code that I don’t really want to repeat. I’ll paste it and I’ll explain it.
Clayton (12:57.262)
So this is our deploy contract helper function. So all it’s gonna do, it’s gonna read the contract file. It’s going to load the SolC compiler version that we need. It’s gonna compile the contract. Then it’s going to deploy that contract to whatever layer we want. So it will pass in, sorry, the layer, the dev account, the filing and the contract name, and it will use all of that to deploy the contracts. It will then let us know if it deployed it.
Then return the contract so we can use it later in our code. So far, we’ve only deployed the L2 contract. We need to deploy the L1. So I’ll do that really quick. As you probably guessed, it’s very similar to deploying L2. We just give it a different layer. Same dev account because, again, we bridged it to ourselves between the layers. Then the L1 read balances contract with the L1 rebalances.
Contract within the file so that once those are once those two are deployed, we can now use them so what we want to do is we’re going to want to now get our address balance of our Bitcoin regtest miner and send that balance down or send that to the L1. so Given the L2 contract. We’re going to call send Bitcoin address balance to L1 with the L1s contracts address
And then the address, the Bitcoin address that we want to get the balance of, which again, was set up here. It’s our Regtest miner address. So again, just to trace this down, what this will do is we’ll call send Bitcoin address balance to L1 on L2. This will use the cross-domain messenger. Well, it will grab the balance from that address, use the cross-domain messenger to send that to the L1 rebalances address that we pass in. It’ll call set Bitcoin balance address.
With the block number, the address, and the balance, which we’ll then store it in L1 storage, and we can read that later. Okay, so now that we’ve got that deployed, we will… Now this is where we need to get a little more… This is where we need to use the Optimism SDK. So once we’re setting this down, we actually have two more steps we need to perform. One is we need to prove the message to L1. So using the SDK, we just call it a prove method.
Clayton (15:23.458)
And then we need to finalize it. And once those two steps are complete, we’ll be able to read that data from the L1 as it was sent down. So on the mainnet, this process takes quite a few days, actually, with Ethereum. But on LocalNet, it just takes a few seconds. So you can rapidly develop that proven and finalized. So I’m going to.
add another helper function really quick that I added because using that Optimism SDK, they didn’t really give you, in my opinion, many ways to debug what statuses were while you’re waiting for a new status. And what we’re going to want to do is we’re going to be able to. We need to wait for a message to be ready to prove it. Then actually I’ll just paste the code. These are just following that way. So we’ll grab this.
Clayton (16:26.456)
So once those are deployed, we will go up here. We’re going to then, now that these contracts are deployed and we’ve called the method, we’re going to wait for the status to be ready to prove of the send this from L2 to L1. Once that’s waited for, we’ll prove the message and then we’ll wait for it to be ready for relay. And we’ll finalize the message. And once it’s relayed,
Now we can actually read data from L1 that we got from L2. So I’m going to copy the last bit of this example here.
Clayton (17:05.068)
Paste it right down here. So at this point, I just grabbed the L2 Bitcoin address balance. And then I grabbed the L1 contract address balance that we sent down. And I compared the two. And what we’re expecting is that the L2 balance is going to be a little more or equal to the L1 because we sent it down. And between that time period, we could have mined more Bitcoin.
But yeah, that’s how you would send data from L1, or I’m sorry, L2 to L1 and read it using the Hemi network. If you want to run LocalNet net, it’s as simple as cloning a repository and running Docker Compose up on end-to-end Docker Compose.
This will take a little bit the first time it’s got to pull the images and build them But once it does you can then run the example and you should get output something like this So can see we bridged our eth to hemi -eth And then we deployed our L2 contract here. We deployed the L1 and then we sent our message, so here’s where we waited for a while to see if we’d be ready to prove it. We proved it. We’re waiting for it to be ready to relay. We relayed it and then we got the
In this case, the balances were equal. So this is reading from L2 and this is reading from L1. So we got the L1 balance at L2 height on LocalNet at 169, this much Bitcoin. And yeah, we’ll send a link to the example code after the webinar. But that’s how easy it is to develop on LocalNet on Hemi. So I hope this helps and we’re here to answer questions. And yeah, I hope you all come up with some really cool ideas using this technology. If you have any questions about the technology, we can answer them. We also have a roadmap of things we’re going to implement. But yeah, thank you very much. I’ll pass it back to Erin.
Erin (19:11.928)
Thanks Clayton. All right guys.
So we are going to funnel some questions in. I just wanted to start off by thanking Clayton for that webinar. That was really nice. So one of the questions we wanted to ask is, know, on the L2 front, why can’t I trust this Bitcoin data?
Clayton
Yeah, yeah, that’s a good question. And that’s kind of a cool thing. You’re not really trusting it, right? So your L2 node becomes part of the Bitcoin peer -to -peer network, right? So you are joining the network, you’re connecting to peers, you’re building the chain yourself. You’re becoming a full Bitcoin node yourself. So the data you query is actually your own determination of what the Bitcoin chain is. So yeah, you’re not relying on any third party to do so. You’re part of the network yourself, you’re building that data yourself and you’re trusting only yourself and the data that you’ve built from the chain.
Errin
Awesome. We did have a question here from someone in the chat asking, so for some clarification, do we need to change the RPC to Local Port or does the rest of the contract stay exactly the same?
Clayton
So when you’re working with LocalNet, all the contracts or not the contracts, all the services.
The daemons will run in Docker and they’ll expose themselves to a port on your local host. So when you try to interact with any of the RPC endpoints, you’re going to want to interact with the ports that are mapped to your local host. And if there’s any questions about which port might be the one that’s mapped, just take a look at the Docker compose config file and you’ll be able to see that port mapping. So you’ll be able to find the one you want. So yeah, just make sure you’re connected to those when you need to grab data from LocalNet.
Errin
Awesome. Another question is, how can I simulate calling another contract locally?
Clayton (21:10.811)
Yeah, what do you mean? Do you mean like one contract calling another or do you mean like calling a contract from like an RPC endpoint?
Errin
That was a question from Alix. Yeah, Alix, if you want to clarify your question.
Clayton (21:34.432)
It’s OK. I think I can answer in both scenarios. When you want to, so, what was the question? How can I call another contract? That’s what it was, Yes. How can I simulate calling another contract locally? Yeah. If we want to call, so let’s say you want to call a certain contract, you can deploy that contract to your local network. So if that’s an L1 contract, you can deploy it to your L1. You can deploy it to your L2. And then you would call it just the way you would in Testnet.
If you want to call it from another contract on the network, just deploy that other contract and call them as you would on testnet. If you want to call the contract from an RPC call, you can use the Web3 provider to do so. Either of those should work. So it’s just like testnet, but it’s all running locally. So if you can deploy it to testnet, just deploy it to the daemons locally or interact with those daemons and you should be able to do it.
Errin
Awesome. Well, thank you so much, Clayton, and thank you all for attending today. If you enjoyed today’s webinar and want to stay updated with more insights, tips, and some of our future events, please be sure to follow us on X at hemi.xyz and visit our website hemi.xyz. If you have additional questions or want to continue this conversation, please reach out on Discord and our team will get back to you.
But with that, we look forward to seeing you at our next webinar and I hope everyone has a great day.
Thanks everyone. All right. Thanks everyone.
Clayton (23:12.418)
Thank you.