Loading video player...
If you're a developer in 2026, then you
have to be paying attention to
blockchain. It's one of the most in-
demand skills out there. And if you're
writing smart contracts or building
DAPs, then there's a new way to do this.
There's a new library that's been
gaining a ton of steam that you have to
pay attention to. That's completely
changed the game for how to write a
blockchain application. Well, what is
it? It's called VM. And in this video,
you're going to get a full step-by-step
guide on how to get up to speed with
this so that you can increase your value
as a blockchain developer. So, if you're
new around here, hey, I'm Gregory. And
on this channel, I turn you into a
blockchain master. So, if that's
something you're interested in, then
smash that like button down below and
subscribe. And if you want to see how to
jump on this rapidly growing tech trend
and increase your income and work
remotely and become a blockchain
developer, I can show you how to do that
step by step from start to finish over
at adaptiversity.com/bootamp.
All right, so let's get into this. So,
before we start coding, you know what is
VM? Well, if you're writing a blockchain
application, let's say in JavaScript,
for example, it's not going to talk the
blockchain out of the box. You actually
need a special library and interface to
do this. And you know, you might have
seen stuff like ethers.js or if you've
been around for a while, web 3JS and
other, you know, things like WagMe.
Okay, that's what they all try to do.
Basically, turn your application into a
blockchain application. Uh, but they all
have a certain set of problems
associated with them. And that's why VM
was created to try to fix those
problems. So basically, this is an is a
JavaScript library that turns your app
into a blockchain application and tries
to one up the previous iterations of
this to actually make it better. So you
can, you know, click on the VM homepage
and kind of look at a list of these
problems and how they've tried to solve
them. I won't belabor them in this
video, but what you're going to do is
just see actually how to use it and what
the benefit is by actually building
something with the library. So I'm going
to invite Justin from the DAP University
team to take you over the shoulder and
show you exactly how to do that. All
right, so let's go ahead and talk about
what VM is and how it's used in Ethereum
development. Now, right now, as you're
watching this video, you're probably
watching on your computer and you're
probably using a browser like Chrome or
Firefox. Your browser is displaying a
front end which is written with HTML,
CSS, and JavaScript. And this front end
is talking to a backend like a database
to fetch videos, comments, etc. And this
is an example of a typical web 2 setup.
Now, when it comes to web 3, you're
going to have a similar setup. You're
going to have a browser, which displays
your front end, which may also still
talk to a backend, like a database, but
you'll most likely also have your front
end talk to the blockchain. You might
fetch data on chain to display on the
front end. You might have users on your
front end interact with the blockchain
by signing transactions. And this is
where VM comes into play as it will help
our front end talk to the blockchain. So
VM is a JavaScript library. I'm
currently at their docs which you can
also visit at VM.sh. You'll get a little
bit of an overview of what VM is. You
can also click on why VM and you'll see
why this team decided to build VM. I do
encourage you to give this a read on
your own time. Now, as we move through
this tutorial, we are just going to
build some simple scripts that will use
VM to interact with the blockchain. And
you will want to make sure to have the
VM docs open because we are going to
heavily reference the docs as we move
through this tutorial. All right, so
let's go ahead and walk through the
project setup. So I'm currently at the
repository for this tutorial, which is
at github.com/gapuniversity/vm-examples.
In this repository, you'll be able to
see things like the final code solution.
So for example, we can go into this
examples folder.
We can look at the first script. And
here you'll be able to see the final
code solution. At this repository,
you'll also be able to see the readme
and you'll be able to see some of the
tools we're going to work with as well
as the setup and some of the steps for
running the different scripts. Now, for
this tutorial, you are going to need
NodeJS. So, if you don't have it
installed, you can click on this link in
the readme.
This will take you to Node.js's website.
You can click on get Node.js and then
you can select your platform. So if
you're on Mac OS, you can select Mac OS.
I do recommend you use NVM with npm and
it will give you the commands to run in
your terminal to install NVM and NodeJS.
If you are on Windows, we have a little
note here. I do recommend you install
WSL Ubuntu which you can also click on
this link. This will take you to the
Microsoft docs and this will give you
the steps for installing WSL onto your
Windows machine. And once you have
installed WSL back at Node.js's JS's
website, you'll actually select Linux
NVM with npm and it will give you the
commands to run in your WSL environment.
If you do want to learn more about NVM,
which stands for node version manager, I
also have a link here in the readme. So
you can click on this link and this will
take you to the NVM documentation. You
can see an example of using NVM. They
also walk you through installing NVM as
well as some troubleshooting in case you
have any issues with installing it. The
last tool we'll need is Tenderly so we
can spin up our virtual test net. Don't
worry too much about what that means for
now. We'll walk through this in a little
bit. Now, if we scroll back up now, I
showed you how you can look at the final
code solution, but in this tutorial, we
will be coding along. And so, there is a
second branch. By default, you'll be
looking at the master branch. But if we
click on this dropdown, we'll have a
second branch here called starter code.
If we click on this, this will be the
repo that we'll work with or that we'll
code along with. And we can even look at
the examples folder. And we look at the
first script, we can see there's nothing
coded out here. And there is just
comments of what we'll need to code. So
if I go back, we'll need to get this
code base onto our computer. So if we
click on this code button, we can
download the zip folder to download the
code. Although the more easier way will
be just to clone it. So we'll copy this
HTTPS link. And you'll go to your
terminal. Keep in mind again I am
working in Windows WSL. So your terminal
might look a little bit different if you
are on a different system, but the
commands it will execute should be the
same. So to clone this repository, we'll
do get clone. We will paste in the link
we copied. So you can do control +v.
Keep in mind that this command will
clone the master branch, but we do want
the starter code branch. So we'll do -b
and then we'll do the name of the branch
which will be starter code. So the full
command is get clone the URL of the
repository
b for branch and we are cloning the
starter code branch. We'll go ahead and
execute that. And we can see that has
been cloned. We cloned it into a folder
called VM-amples.
So we should be able to do cd and then
the name of the project. So if I do VM
and then I do tab, should autocomplete
it for me. Press enter. You can see it
updated my path of where I'm at. Can
also do pwd to confirm. So this is where
I'm located in the terminal. And if I do
an ls,
we can see the files and folders
associated with this project. All right,
so we'll come back to the terminal in a
bit. Back at the repository, however,
one thing that is not listed here for
the initial setup is a text editor. So
you are going to need a text editor of
your choice. So if you can use Sublime
Text or Visual Studio Code, I am going
to be using Visual Studio Code. So, if
you don't have it, you can go to their
website and you can download it and
install it. And once you have Visual
Studio Code installed back in our
terminal, we should be able to open up
the project with a command. And we
should be able to do code dot and that
should open up your text editor like
this. Keep in mind if you are on
Windows, it may ask you to install the
WSL extension for code. So if you see a
prompt like that maybe in the bottom
right and go ahead and install it. But
here in our text editor you should see
that we are in VM examples you can see
our folders here as well as some of our
files. So the first thing we'll walk
through in our project will be our
package.json
file. So if we click on this it'll open
it up. And the main thing we want to
focus on here is the dependencies. So
these are the tools or libraries that
our project will depend on. Of course,
our project focuses on VM. So we have VM
listed here as well as the version of VM
we'll be using. We have two other tools
here. Environment
variables and prompt will be for getting
input from our terminal. If you're not
sure what any of that means, don't worry
about these just yet. We'll walk through
this a bit later on. Now, before we even
begin to code, we will need to install
these tools. So, we can open up our
terminal. I'm going to go ahead and
clear this. Again, make sure you are in
your project. So, to install our three
dependencies, we're just going to
execute one command called npm install.
And when we run this command, this will
look into our package.json, JSON and it
will look at our dependencies section
and install everything that's listed in
here. So we'll go ahead and execute
this. Your output should look something
like this. And you can see we get a new
folder in our project called node
modules. And this node modules folder
will house these dependencies. And also
fun fact, we can also execute npmls and
it will list the dependencies or tools
that are installed. All right, so let's
go ahead and close out our package.json
and we can start taking a look at our
first script.
And you can see I have some comments in
here of some of the things that we'll
want to do. So for our first script, we
just want to learn how to set up a basic
connection and then read some data from
the blockchain. So we'll fetch the
current block number and then we'll get
the balance of an address. So let's go
ahead and go to the VM documentation.
Their documentation is atvm.sh.
We'll click on get started. We see that
they give an overview on what VM is.
They tell us how to install it, which
we've already done, and they give us a
quick start guide. And we can see they
give an example of how to set up a basic
connection. So you can see that we need
to set up a client with a desired
transport and chain. And so what we'll
do is we'll just copy this for now and
just bring this straight into our text
editor. So we'll paste this into our
script. So it looks like there's a lot
going on here, but let's go ahead and
kind of break this down a bit. When
we're working with VM and we want to set
up a connection to the blockchain, VM
has these clients. And we can actually
see in the side menu here under clients
and transports, we can see there's a
public client, a wallet client, a test
client, or even custom clients that we
can build. Now, since our script is
pretty basic as we're just reading data
from the blockchain, we're going to work
with the public client. So on VM, we
call this create public client function,
which of course we import from VM. This
function takes in a object with two key
value pairs and the first key is the
chain and this represents what chain we
want to connect to. Now VM is nice
enough to define some chains for us. So
this mainet we also import from VM
chains and they also define other
chains. So if we wanted to use like bass
for example, you can import bass.
And then if we wanted to connect to
bass, we'll just replace mainet with
bass. And this would be an example of
creating a basic connection with base.
But for now, we'll just leave it back to
mainet. The second key is transport. Now
transport is supposed to represent a URL
to the blockchain node. So remember
whenever you talk to the blockchain
you'll be talking to a blockchain node
usually through a URL and when we define
the transport for a client we call this
HTTP function which is also imported in
from a VM and by calling this function
as is this will connect us to a public
blockchain node. And one thing to note,
if I hover over this, we can see the
definition for this function. And
there's a lot going on here, but we can
see that when we call this function, we
can pass in a string, which would
represent the URL to a blockchain node.
And we can see this question mark here,
which basically means that this is an
optional thing we can pass in. Now, for
this script, we're not going to pass in
a URL. But do note when you are working
with clients, especially if you're
submitting transactions, you should
always pass in your own URL. And if we
look at the VM docs, we can see that
they say here in a production app, it is
highly recommended to pass through your
authenticated RPC provider URL. If no
URL is provided, then VM will default to
a public RPC provider. Now because we
are just reading data from the
blockchain using the public RPC provider
and not passing in a URL here is totally
fine. So now that we are calling create
public client this function will return
to us the client connection and we store
this client in this variable called
client. And now that we have this client
now we're able to perform actions on
this client. So if we go back to the VM
docs and we scroll down, we can see that
we can now consume actions and we can
actually see how they get the block
number. So after they define the client,
they then call client.get block number.
So we can go ahead and copy this line
down here and we'll bring this into our
script. So again, we call this create
public client. We store this in this
variable called client. This variable
called client now has a bunch of
functions that we can call on it. And
one of them to get the current block
number is the function get block number.
Now because we are fetching data from
the blockchain, of course it takes time
for that data to be returned back to us.
And because if I hover over this get
block number, we can see this returns a
promise. This means we will need to
await for this function to finish get us
the block number. And then once we have
the block number, we can store it in
this variable called block number. And
then we can go ahead and console log
this so it can be outputed in our
terminal. So right below this line, I
will add a console log. I will do back
tick. Then I'll say current block
number. I'll do a dollar sign and curly
brackets. And this special syntax here
means I can put logic inside of this
curly brackets. So I can use our
variable block number here. And now we
can go ahead and test our script. Of
course, make sure we save our file. So
file and save. Go to our terminal. Of
course, make sure you are in the
project. And to execute our script,
we'll do node. Our script lives in the
examples folder. And then one
publicclient.js.
So if I do one and then tab, it should
autocomplete. And then I can press
enter. And look at that. We get the
block number log to us. We can add a
little bit of formatting to our console
log if we wanted to. So I can at the
very beginning of our string I can do a
back slashn and then at the very end of
our string I can do back slashn
save the file and then rerun our script
and this gives us some empty lines above
and below our console log. So back to
our script there is one more thing that
we'll want to do and we'll want to get
the balance of an address. So if we go
back to the VM docs, you'll remember
that now that we have defined this
client, there are functions we can call
on this client such as get block number.
Over in the side navigation here, if we
scroll down, we can see a section here
called public actions. If we click on
this, it opens up a dropdown.
And here we can see some of the
functions we can call on this public
client. So we called get block number.
We can see that this function is listed
here. Get block number. We of course
want to get the balance of an address.
So if you're looking at this list, you
might already see the function that
we're going to call and that would be
under account. And this would be get
balance. So we click on this. So we can
see an example of how they call a get
balance. We can actually just copy this
line and we'll paste this into our
script. So underline 15. Now in their
example the name of their client is
public client but for us we just called
it client. So we'll rename this to
client. So we're doing client
getbalance. This function takes in one
argument which is an object with one key
value. The key being the address. And
this is a string of the address we're
going to get the balance of. Of course
it takes time to get data from the
blockchain. And this get balance
function returns a promise. So we await
for this function to finish and get us
our value. And then once we have that
value, we store it in this variable
called balance. So now that we have the
balance, let's go ahead and add a
console log. Then I'll say eat the
balance of. I'll do a dollar sign in
curly brackets. And we're going to
refactor this statement a little bit.
We'll store this address in its own
variable. So right above where we call
get balance, I'll say const address.
This will be a string. We'll copy this
address and paste it in here and we'll
reference the variable instead. So
client get balance address address. So
because this key value is sharing the
same name, the key being address and
then we're using a variable called
address, we can simplify this further
and just say address. And then in our
console log in the curly brackets, we'll
say address. So each balance of this
address and then I'll do another dollar
sign in curly brackets. And then we can
say balance. And then we can add a new
line at the very end. Let's save our
script. And let's go ahead and run this
in our terminal. All right. And there we
go. We get the current block number. And
then each balance of this address is
this very long number. Now, one thing to
note, the reason we get this very large
number is because this is going to
return us the balance in way. And if we
go back to the VM documentation, we can
see this little note here or shows us an
example of how this function will
return. So it returns a very large
number. And this large number represents
the ETH balance in way. And we can also
see that from the description up here
returns the balance of an address in
way. Now we is the smallest unit of
ether. And if that doesn't make a whole
lot of sense, it might be easier to show
you. So if we go to eatconverter.com
so this will be eat-converter.com
we can see that there's different units
of ET right so the three most common
ones of course is way guay and ether so
for ether if I were to put one in here
this would represent one E we can see
this converter will convert this one E
into other units we can see how one E
would be represented in way so it'll be
this one plus a whole bunch of zeros at
the end. Now, by default, ETH is 18
decimals. So, this is basically just 18
zeros added on. And then if I had like
one way of e, we can see what the ether
value would be. So, in our terminal, we
got this very large number. This is in
way. So, let's actually just copy this.
Come back to the e converter and we'll
paste this in for the way value. And we
can see that this value is basically
0.16
eat. So what we'll want to do is we'll
want to convert this wave value into
ether value. Now luckily for us, VM has
some utility functions that can help do
this conversions for us. So back in the
VM docs, if we scroll all the way down
in this side navigation to utilities
and we scroll all the way down again
and we look for units, we can see they
have some helper functions here. And the
one we'll look at is format units. And
we can see an example of how they use
format units. They import it in from VM.
And then they call format units which
takes two arguments. The first value
being the way value and then the amount
of decimals to convert. So for their
example, they're doing nine decimals. E
of course is 18 decimals. So what we'll
do is we'll just copy this line and in
our script in our console log inside of
the curly brackets here we'll paste it
in and we'll replace this number with
balance and then because e is 18
decimals we'll replace the 9 with 18. So
we'll save our file and if we rerun this
I'll clear my terminal
and of course we forgot to import it. So
at the very top we'll say format units.
And now we'll save our file and rerun
this. And we can see our ETH balance is
now formatted by 18 decimals. And this
makes it a lot more easier to read. So
this address has 0.16
E. Now before we move on to our next
script, the last thing I want to show
you is how you can use AI to kind of
help you learn a tool. Because obviously
if you're working with for example VM
for the first time you might not be
aware of this format units or you might
not even be aware that there is a
function that can help you convert
between way and ether. So as an example
I'm just going to go ahead and copy
these lines of code and we'll go over to
an AI. I'm going to use chat GPT. You of
course can use whatever AI you want and
I'm going to give it a prompt. So my
prompt I'm working with VM and
JavaScript to create some scripts to
read data from the blockchain. I'm
getting the balance of an address. Give
it the code that we copied. I added a
little console log balance here. The
following code returns a very large
number. Why is this? And we'll see what
chat GPT says. And we can see it
explains it pretty well, right? You're
seeing a very large number because
balance returns the balance in wei, not
in ether. Ethereum stores balances in
way, the smallest unit of ETH. And it
gives us an example. And it even gives
us a example of how to convert from way
to ether. And we can see the
recommendation here is actually to use a
function called format ether. So in our
script we use format units. They're
recommending we use format ether. So
what we could do right based on
recommendations that an AI gives you,
we'll just copy the function that
they're recommending. We go over to the
VM docs. If we do arr f to search, paste
in the function that they recommended.
We can see that VM does have a function
called format ether here. And if we
click on this, we can see an example of
how to use it. And we can see we can
also use format ether for converting
from way to ether. Now in our script, we
did use format units. The reason I chose
to use format units instead is because
it's a little bit more explicit, right?
You have to pass in the amount of
decimals that you want to convert. So
format ether by default converts 18
decimals which is perfectly fine for our
script because we are working with e but
format units is just a little bit more
explicit so that way you know you're
converting 18 decimals later on when we
work with different tokens in some of
the future scripts uh we will not be
working with 18 decimals. So I just
wanted to give you an introduction to
this function instead. But all in all, I
wanted to show you how using an AI can
be beneficial if you're learning a new
tool or language. And based on the
recommendations that an AI might give
you, using documentation to kind of
cross reference with the AI, as
sometimes the AI and their
recommendations aren't always correct. I
would also recommend you use AI to help
reinforce some of the concepts you're
learning. So, if there's some things in
this script or maybe even future scripts
that you're not sure of or maybe you
have questions on, copy and pasting your
code, putting it into the AI, and asking
your AI questions to help answer your
questions can be super beneficial. All
right, so let's go ahead and start
working on our second script. So, we'll
open up our second script. And just like
the first script, we have some comments
in here of some of the things that we
want to do. For this script, our goal is
to start booking with the wallet client.
So remember in the first script, we
worked with the public client. This one
will be an example of the wallet client.
We have some comments in here of some of
the things that we'll want to do. And
this time we have some boilerplate code
already written for us. Now just like
the first script, we have some imports
in here. We are importing similar things
like HTTP and mainet. And unlike the
first script where we imported create
public client as we can see here for
this script we are importing a new
function called create wallet client and
we'll look at the docs for this in just
a little bit. Now you'll see this
boilerplate code here where we are
importing this prompt for key function
from this helper folder and we are
prompting for the private key. Now, when
you work with a wallet client, you'll
typically be using it for sending
transactions. And in order to send
transactions, you have to sign
transactions with your private key. And
this prompt for key function that we're
calling will allow the terminal to ask
us for the private key. And the reason
we are doing it this way is because we
don't want to store our private key
directly into this file. So we wouldn't
want to do something like this because
if you ever share your code or if
someone ever sees your code, they'll be
able to directly see your private key.
And we can take a look at this prompt
for key function. If we look in the
helpers and prompt.js, you can see that
we import prompt from prompt. You'll
remember in our package.json,
this is one of our dependencies. And
then in the script, we define this
schema, which is this object. We're only
taking in one input, which will be this
private key. Our terminal will ask us to
enter private key. Of course, it's
required. And as we type in the
terminal, it will be hidden or it won't
show what we're typing. And then this is
the function we are exporting. Prompt
for key. We call prompt.st start. We
call prompt.get passing in the schema
that we defined. We get the result which
will be whatever we typed in the
terminal. And then we return the private
key. And just for demonstration, we can
actually go ahead and just run the
script as is. I'll add a little console
log below this line though. And I'll say
console log private key. And if I go
ahead and run the script, you can see we
get this prompt enter private key. And
so in this I can just so I can type some
random letters and numbers.
And of course, as I type, it stays
hidden and our terminal doesn't show it.
But if I press enter, you can see that
it logged what I typed. So I'll clear
this and back in our script, go ahead
and remove this console log. All right.
So let's go ahead and start taking a
look at this create wallet client
function. So if we go to the VM docs and
under clients, we'll click on wallet
client. It gives us a little overview on
the wallet client, you can see how to
import it, which we're already doing.
And then it gives us an example of how
to use or how to define your wallet
client. And you can see this is very
similar to how we defined our public
client in the first script. So after
they import create wallet client, they
call this function passing in the chain
and the transport. You might notice for
transport instead of calling HTTP, they
call a different function here called
custom and they pass in this window.
Now this window.ethereum is if you are
using your browser. So, let's say you're
building a front end and you need to
have a user sign a transaction. You
would define your wallet client using
this window. Ethereum to reference
something like MetaMask. And we can also
see that it says it up here at the top.
An example could be sending a
transaction via a browser extension
wallet like MetaMask with the window.
Ethereum provider. Now, in our case, we
aren't working in the browser. We are
working on scripts directly on our
computer. So this example that they
shared here doesn't really apply to us.
So if we scroll down a bit more,
we can see that they have a section here
for local accounts. And if we scroll
down, we can see the example they shared
here. So they import create wallet
client. They call create wallet client
passing in the chain and transport. And
this time for the transport, they just
pass in HTTP. And then once they have
the client defined, they then define
this account variable where they call
this private key to account imported in
from VM accounts and they call this
function passing in the private key. And
then finally once you have your client,
you have your account, you can then
perform an action on the client just
like the public client where they call
this send transaction function. Now with
this example they showed they define the
client first and then they define the
account. But if we scroll down you can
optionally define the account first and
then define the client. And the reason
for this is so you can hoist or attach
the account directly to the client. So
that way when you do an action on the
client like sending a transaction you
don't have to manually pass in the
account. So for our script, this is the
example that will follow except we're
not going to do a transaction. So we can
actually just go ahead and copy these
lines and paste this into our script and
I'll move this line up. We of course
want to import this private key to
account function. So let's go ahead and
do that. So import private key to
account from VM accounts. And when we
call this function, we need to pass in
our private key which we are currently
storing in this private key variable.
So, let's go ahead and say private key.
Now, later on, there might be an issue
with how we're doing this. So, we might
have to come back and make some changes,
but for now, I'm just going to leave it
as this. All right. So, now that we have
our account and we have our client, now
we're ready to perform actions on our
client. Now, for this script, we're not
going to do a transaction. We're just
going to go ahead and just get the
address that's tied to the private key.
So, if we go back to the docs, they did
show us an example of this earlier. So
if we scroll up, we can see that they
call this get addresses function which
will return an array of all the
connected accounts. Now remember for
this example, they're using
window.ethereum. And when you're working
in the browser and you're working with a
wallet like MetaMask, you might have
multiple accounts connected. So their
function get addresses would return all
of the connected accounts. And that's
why this function returns an array. And
even though we're not using
windowethereum, this function should
still work for us. It'll just return one
account or one address. So let's go
ahead and copy this and we'll paste this
into our script. So we call client get
addresses because this returns a promise
we await it. And since we'll get a list
or an array of addresses, even though in
our case it'll just be one. We
dstructure or extract only the first
value by doing these square brackets and
then passing the name of the variable
that we want to assign it to. And now
that we have this address, we'll just go
ahead and console log it. And we'll go
ahead and save our file. And we should
be able to run our script to see if it
works. So in our terminal, let's go
ahead and run it. And of course, we'll
need to enter a private key. So I'm just
going to do one of my developer accounts
in MetaMask. This wallet that I have set
up is my developer wallet. You should
also have your own wallet that is
separate from your personal wallet. And
your developer accounts you use should
have no money in them. Now, if you don't
have your own developer wallet, I would
recommend you create one or have one.
So, if you want to use something like
MetaMask, you can download a different
browser. I believe with Chrome there's a
way you can create different profiles
and you might be able to set up a
different MetaMask or maybe if you've
built different projects in the past and
you might have something like Anvil
installed you could open up a new
terminal and do something like Anvil
which will spin up a fake blockchain and
it will give you some dummy keys.
Whichever way you choose to get your key
is up to you. Just make sure that the
key that you get is not one of your own
personal accounts. So if you're using
something like MetaMask, you can just
select one of your dummy accounts. And
with your dummy account in MetaMask, you
can click on this hamburger menu and do
account details, private keys, and then
you can enter your password to get your
private key. So once you have your
private key back in your terminal,
you'll go ahead and paste it in.
Remember that your terminal is not going
to show what you paste it in. But if I
press enter, you'll see that I actually
get an error here. Now, I think I
already know the reason why I got this
error. And you might also get a similar
error if you copied and pasted your
private key from MetaMask. And you can
see the error I got here was invalid
private key expected hex or 32 bytes,
but got string. And what we could
actually do with this error is we could
actually ask chat GBT or an AI on why we
got this error. So my prompt goes like
this. Hey, I am using JavaScript in VM
to build some basic scripts to interact
with the Ethereum blockchain. I'm
setting up a W client and I'm using a
helper function to ask for the private
key in the terminal. This is my script.
And then I paste in the entire script.
When I execute, I paste in my private
key I got from MetaMask and I get the
following error. Why is this? So let's
go ahead and see what chat GPT says. So
we can see chat GPT says MetaMask does
not export a raw private key. It exports
it without the 0x prefix. VM's private
key to account requires one of these
formats. 0x prefixed 64 character hex
string like this. But MetaMask gives us
something like this without the 0x. And
we can actually test this to see if
chatbt is correct. So actually in our
terminal and we'll rerun the script. I
will fetch back the private key I got
from MetaMask. And before I paste in the
key, I'll just type zero X and then I'll
paste in my key. And let's see if this
works. And it looks like it did. So it
looks like chat GPT is correct. Looks
like if we were to get our private key
from MetaMask, it is missing the 0x at
the beginning of the key. And based on
my response, you can see it gives me
some suggestions. So we can do something
like raw key equals await prompt per
key. And then we can do something like
raw key starts with testing to see if
our string starts with 0x. And if it
does, we set private key to the raw key.
Otherwise, we say 0x and then append the
raw key and set that as our private key.
So I actually really do like this idea.
We'll just go ahead and copy this and
we'll paste this into our script. And if
you're unsure what this starts with
function is, we can actually ask chatbt
to elaborate more on this. So can you
explain what the starts with function
is? So starts with is a built-in
JavaScript string method. It checks
whether a string begins with the given
substring. And then we can see it gives
us some examples here. So again, if
you're unsure of what some functions
might be doing, chat GBPT is great at
explaining it. Of course, you can also
cross reference with documentation. So,
since this function is part of
JavaScript and we'll search JavaScript
starts with and the JavaScript
documentation is at mosilla.org
and we can see this function will also
help explain what this function is. And
so, let's go ahead and test our new
script and see if this will work. We'll
do file and save. And let's run the
script one more time. Go ahead and get
your private key. And there we go. Looks
like it worked as is. So, just to recap,
our terminal will ask us for our private
key. We check to see if the private key
that we submit starts with 0x. If it
does, we set the key that we inputed as
our private key. If it does not start
with 0x, we add 0x to the key that we
submitted. Set that to private key. We
call this private key to account that is
imported in from VM accounts. passing in
the private key. This function will
return this account object. We then call
create wallet client passing in the
account, the chain and the transport.
And then finally, once we have our
wallet client, we can then do actions on
our client and we called get addresses
to get the address that is tied to our
private key. And that completes our
second script. All right, so let's go
ahead and start taking a look at our
third script. Now, our goal for this
script is to send a basic transaction
where we'll send ETH from one account to
another account. And just like the last
script, we have some comments in here of
what we'll want to do. And we have some
boilerplate code in here, which is very
similar to what we did in the last
script. So, we have our imports up here.
We have a new import, which is envig.
We will come back to this a little bit
later. We import our function for
getting the private key and then we get
the private key and format it. We set up
our wallet client and we get the address
tied to the private key that we
provided. Now before we start looking at
the docs and writing in code, you might
be wondering how we're going to send ETH
from one account to another. You should
remember that the accounts that we are
working with should have no real funds
on them. And these accounts should only
be for testing purposes. So if we don't
have any real funds, how are we going to
test? And no, you do not want to
transfer real funds to these accounts
because then you're just going to be
wasting money trying to test your
project and that will be very annoying.
So what we're going to do instead is set
up a fake blockchain or test net and we
are going to fund our accounts with some
fake ETH and we will use that test net
to test our transactions. Now if you're
used to working with hard hat or foundry
you might know you can spin up a fake
blockchain with the hard hat node or
anvil and that is a totally valid option
for testing these scripts as well.
However, I'm going to introduce you to a
different tool. And this tool is called
Tenderly. And Tenderly will be the tool
we use for spinning up this fake test
net. So, in the top right, we'll go to
dashboard and go ahead and log in or
create a free account if this is your
first time. And once you have signed in
to your Tenderly account, you should
have a dashboard page something like
this. Keep in mind though, if it's your
first time, you might have to create a
project. So you might have to do this
first and then once you have your
project you'll have a dashboard like
this. So there are a lot of things you
can do with tenderly. Our main focus is
setting up a test net. So you'll see an
option on the left side here called
virtual test nets. And of course keep in
mind websites are always changing. So in
the future this might look a little bit
different. So if you're watching this
tutorial and the interface looks very
different, just look for something
called virtual test nets. And once you
find it, we'll create virtual test net.
It's going to ask us what the parent
network should be. So if we click this
drop down, we'll go ahead and select
mainet. And what this will do is this
will make it so our test net will have
all of the history of the real Ethereum
mainet. And of course, you can select
different networks if you want, but
we're going to stick to just Ethereum
mainet. Go ahead and give your test net
a name. For chain ID, we'll keep it to
default. So, since our parent network is
Ethereum mainet, our chain ID for our
test net will be one. The public
explorer, we can keep off. State sync,
we will keep off. Go ahead and select
the region closest to you. And we can
ignore this setting. And in the top
right, we'll click on create. So now
that we have our test net created here,
there is a lot going on, but we can see
that our test net is forked from mainet.
We have a chain ID of one. We can see
the current block number our test net is
forked to. And we can also see the URL
for our test net down here. Now these
URLs is what we'll use to connect or
interact with our test net. So you might
remember in our earlier scripts. So if I
look at the first one when we define a
client we define this transport and we
call this HTTP function. Now initially
we didn't pass anything inside of this
function. And since we didn't pass
anything into this function, our client
will connect to a public blockchain node
or public RPC provider. And doing it
this way is totally fine because we were
just reading data from the blockchain.
However, since we will be performing a
transaction, also doing the transaction
on a test net, we actually will need to
pass in our own custom URL into here.
And the URL that we'll pass in here will
be the URL for our test net. So let's go
ahead and copy this URL. We're going to
copy the https URL. And in our project,
you should have this.env.example
file. And you'll see that we have this
variable called tenderly RPC URL. So
we're going to want to make aenv
file. And our env file should have this
variable defined inside of it. And it
should be equal to the URL to our test
net. So let's go ahead and make a new
file. And we'll call it env. And our
variable name is tenderly_rpc
URL. And then go ahead and paste in the
URL. All right. So in your test net,
copy this HTTPS URL and paste it in
here. And once you've done that, go
ahead and save your file. Now we can
define the URL in our script. So I'll
define the URL right after we get the
private key. We'll say const URL. And
this URL will be equal to process.env.
And this will be the name of the
variable in our env. So, tenderly RPC
URL. And now that we have this URL, we
can pass this into our HTTP function
just like that. Now, you might be
wondering why we put our URL for our
test net in thisv file. And there's a
few reasons for this. The first reason
is that it's generally just not good
practice to share your own RPC URLs or
API keys. And even though this is just a
simple test net, for larger projects, if
someone gets a hold of this, they may
mess up your testing or they may even
run up your usage. Now, of course, this
isn't a super big deal if this gets
exposed cuz if it gets exposed, you can
just delete the test net and create a
new one. And of course, we don't prompt
for our test net RPC. So doing something
like this for the testn net URL is a bit
overkill. And the other reason for thisv
is because our future scripts will also
need this same URL. So by having this
env we can define the URL in only one
place. So if we ever need to change the
URL, we don't have to manually go in
each script and change it. we can just
change it directly in the env. And real
quick, the last thing I will note, we
have this new import at the top where we
import this envig. And you'll remember
in our package.jsonv
is one of our dependencies. And in
short, this env package allows us to do
this process.v
followed by the name of our variable.
and it will know to look in our env file
for our project. All right, so now that
we have our testnet URL set up, we can
start taking a look at some of the
things that we want to do. So, we'll
want to get the balances, we will send
our ETH and then we'll get the balances
after. Now, when it comes to getting
balances, we have technically already
seen how to do this. You'll remember in
our first script at the very end we
showed an example of how to get the
balance. So we can follow the same
format like we did here. But there is a
little bit of a problem. You'll remember
that when we called get balance we call
this on this public client. But for our
script since we are sending a
transaction we defined a wallet client.
So under get balances, if I just go
ahead and quickly do await wallet client
and if I do a dot, you'll see my text
editor actually gives me some of the
functions that I can call on this wallet
client. Uh, and there's a lot of
functions here. So if I just do get, you
can see some of the get functions I can
call. And you might notice get balance
is not an option. And just for
demonstration, if I do it anyways, so
I'll say get balance. And what I'll
actually do instead is I will copy this.
Come back here and I'll paste it in
here. And instead of client, we'll say
wallet client. And then real quick, I'll
just do a console log of balance. And go
to my terminal. And let's try to run
this script. It will ask us for our
private key. So go ahead and get the
private key for your developer account
and paste it in here. And you'll
actually see after I paste it in, I get
this type error. Wildclient.getbalance
Get balance is not a function. And if we
also look at the VM docs, you can see we
have public actions and wallet actions.
And for wallet actions, we indeed do not
have get balance as a function we can
call because that only belongs to the
public actions. So you might be
thinking, okay, well, we probably need
to define also a public client in this
script. And that is an approach we can
definitely do. However, that might be a
bit annoying and I am sure VM probably
has a way of handling these scenarios.
So what we'll do is we'll go to our AI
and we'll go ahead and prompt our AI
with our scenario and let's see if it
can give us some recommendations. All
right, so I went ahead and gave it this
prompt. I'm using VM to create some
JavaScript scripts to interact with the
mainet. I have a script that is meant to
send ETH to another address. I've
defined the wallet client to send the
transaction, but I want to get the
balance of ETH before I send the
transaction. The problem is I'm unable
to call get balance on the wallet
client. Here is my code. I pasted in
some of the code. I didn't paste in
everything, just everything the AI
needs. So how I define the wallet client
getting the address and trying to get
the balance which is the problem and
then I say is it possible to avoid
having to define both a wallet client
and public client in the same script. So
let's go ahead and see what chat GPT
says and we can see that it confirms
right. So public actions live on a
public client and wallet actions live on
a wallet client. And it says we can
avoid two separate clients. We only need
to instantiate one client and then
derive the other. So let's see the code
that it gives us. So we can see the
imports. We can see there's a new thing
they were importing here called public
actions. They define the wallet client.
And this looks new. So they're calling
this extend function. And then they're
passing in this public actions that they
imported. And then we can do get
balance. We can see they also give us an
alternate approach here where you could
define the public client first and then
extend it with the wallet actions. So
this of course looks like valid code. We
can confirm this by searching up for
this extend function in VM. So back into
VM docs. This time we'll use their
search bar and we will search for
extend. And we can see this first
suggestion here looks very interesting,
right? So optional extend with public
actions. That sounds exactly like what
chat GPT recommended. And here we do
indeed see in this case you can extend
your wallet client with public actions
to avoid having to handle multiple
clients. And we can see their code
example looks very similar to what chat
GBPT recommended. You create the wallet
client and at the very end you call this
extend function passing in public
actions. So actually let's go ahead and
do this. So in our script at the very
top we're going to go ahead and import
public actions and then where we define
our wallet client. We'll go ahead and do
a dot extend and then we'll pass in
public action. So that looks like all we
need to do for that. So this should work
as intended. Let's save our file. And in
our terminal, let's go ahead and run the
script. And it looks like it failed for
me. The error was invalid params. And
the reason for this is because we can't
directly just pass in sender here. We
would have to do address colon. And now
if I save and let's try to rerun this
again. And there we go. We get zero
because we haven't funded our account
with ETH yet. So, actually, let's go
ahead and do that now. Go back to
Tenderly. Let's go to our faucet. And
this faucet page will allow us to fund
accounts with some tokens. So, go ahead
and copy the address for your account.
Under wallet, paste it in. Remember,
this is the wallet address, not the
private key. For token, we will select
ETH. And then for the amount, it could
be any amount that you want. I'm just
going to do 10. And then we'll click top
up account. All right. So, it looks like
that worked. We can see the transaction
ash here, the account we funded, the
fund amount, and the token that it was
funded with. So, if I open up my
terminal, let's go ahead and run the
script again. And there we go. We get
this large number. Remember, this is in
way. So, we'll go ahead and make this
console log a bit more fancy. So, we'll
say e balance of sender. We of course
format this because we don't want to
read the wave value. Sender balance
before which we'll need to update this
variable. So we'll say sender balance
before. Let's save our file and let's
run the script one more time. And of
course we have to import format units.
And there we go. We get our balance
which is 10 E. All right, let's go ahead
and actually set up our transaction. And
for this we'll just look at the VM docs.
So under wallet actions we will look at
send transaction and we can see their
code example here where all they're
doing is just calling send transaction
on the wallet client passing in the
account the two which is the account
that's going to receive the ETH and then
the value which is the amount of ETH the
account is going to get and remember
that this value is in and not ether. So,
let's just go ahead and copy this and
we'll paste it in. And remember that our
account is already hoisted or attached
to the client when we define the client
up here. So, we don't need to do this.
And we'll also move this address to its
own variable. So, after we define our
URL, let's go ahead and make a new
variable. We'll say recipient. And then
we can copy and paste this address. Of
course, you can change this address if
you want to. We are on our test net, so
it doesn't really matter, but if you
want to set this to your second
developer account, feel free to do so.
But we'll go ahead and replace this with
the name of our new variable. So, you'll
remember that we used format units to
parse a value from way. However, for
this scenario, we would like to parse to
way. And there is another helper
function that is similar to format
units. And if you look at the VM docs,
they have a function under units called
parse units. And we can see their
example here. You can call parse units
passing in a value the amount of
decimals. And this will format your
value into way. So let's go ahead and
add this in. So instead of doing this,
we'll do this. I'll replace this with
one. E is 18 decimals. So we say 18
here. Of course, it might be a bit nicer
to make this a variable. So you could do
something like So you can do something
like amount equals parse units 118 and
then use amount. Here we need to import
this. So up here we will import it. And
once this transaction has been submitted
and finished, we will get the hash of
the transaction. So let's go ahead and
console log this. So console log
transaction hash and then we hash. And
then the last thing we'll do is we will
log the balances after this transaction.
Now this is where I would actually ask
you to pause the video and go ahead and
do this yourself. Of course, we already
have the example of getting the balance
before. So, we can just basically use
this. But I would also like you to get
the balance of our receiver or recipient
and log their balance before and after
the transaction as well. So, go ahead
and pause the video and try to do that
on your own.
All right. So, let's go ahead and log
the remaining balances. I'm going to go
ahead and add getting the balance for
our recipient before our transaction. So
very similar to how we got our sender
balance, we are just passing in the
recipient address instead and then
having a different variable name here
called recipient balance before and then
we will log it. So eat balance of
recipient we format the recipient
balance and then let's go ahead and get
the balances after our transaction. So
just like we got the balances before,
same way we'll get the balance after. Of
course, just a different variable name.
And then we log these balances. Let's go
ahead and save our script. And let's see
if our script will run. Go ahead and get
your private key. And there we go. Look
at that. So we get the ET balance before
for our account, which is 10 E. The
balance of the account we're sending to,
which for me is zero E. It then did the
transaction and logged our hash which we
can see here. And then you can see our
balances after. And you can see we lost
one E plus a little bit extra for gas.
And the account we sent it to does
indeed have one E. So that is an example
of how you can do a simple transaction
like an ETH transfer with VM. All right,
let's start working on our fourth
script. So for this script, we're going
to start learning how to interact with
smart contracts. We're going to focus on
reading smart contract functions and
we'll interact with the USDC smart
contract on Ethereum like we're already
used to. We have comments and some
boiler plates. You'll notice we have a
new import at the top here called parse
AI and we'll revisit this later. And
instead of using a wallet client, we are
using the public client because we won't
be doing any transactions here. We'll
just be reading data from the
blockchain. Now, before we start writing
code, let's just go over what an ERC20
is. So, if you go to ethereum.org,
they'll have a section on ERC20s.
And on this page, you'll be able to read
everything about an ERC20. Of course,
you can also use an AI to help explain
what an ERC20 is, but the main thing we
want to focus on is that an ERC20 will
always have a set of functions you can
interact with. So, every ERC20 token on
Ethereum will have all of these
functions. They'll have a function like
name, symbol, decimals, total supply,
etc. And if we actually go to Etherscan
and under tokens, top tokens, we can see
some of the top ERC20 tokens here. So
these should be tokens that you might
already be familiar with, right? USDT,
B&B, USDC, wrapped BTC, we link etc. Now
for our script, we're going to interact
with USDC. And if we click on contract
read as proxy, we can see some of the
readonly functions for USDC. Now there's
a lot of functions here. But the most
important thing is since USDC is an
ERC20, it must have all of these
functions. So for example, name and
symbol. We can look for it in this list.
We can see name right here. And we can
see symbol over here. So if we take a
look at the VM docs and we look at
contracts since we want to read
functions, we're going to look at this
read contract. We can see an example of
how they use this function. So we can
see they call read contract on the
public client. They pass in an address
which this address is the address of the
contract you are reading. So in our case
since we'll be reading from USDC this
will be the address we are interacting
with. Then they define this AI and then
the name of the function they're
calling. So for this example they're
calling total supply. Now this AI stuff
we see here we can actually take a look
at this AI.ts.
We can see they define this AI and it
looks something like this. Now, ABI
stands for application binary interface.
And all it really is is it's just a
definition of the functions that you
plan to interact with. And this AI
defines the inputs for the function,
which total supply takes none. Of
course, the name of the function itself.
What this function outputs, this
function will output one thing, which
will be a number. And this is a view
function. And of course, you can use
your AI to go a little bit more in depth
on this. But the main thing to
understand is that the AI is just a
definition for the functions that you
are going to interact with. So if you
plan to interact with four or five
different functions, you would have to
include a definition of each function.
Now, writing out an ABI like this might
be kind of annoying. There are different
ways you can get the AI. For example,
back in Ether Scan, if we look at the
implementation contract for this and we
scroll down a bit, we can see the AI for
USDC here. And you can easily just copy
this and like bring this into your
project. Of course, this AI is very very
long. And this includes all the
functions for USDC. Now, luckily for us,
VM does include an easier way of doing
this. We can scroll down and under the
AI section, they have this function here
called parse AI. And if we scroll down,
we can see an easy way of defining the
AI where we can just call parse AI,
passing in an array of each function
that we want to parse. And so what you
see here is an example of a shorthand or
human readable AI. And so we'll do
something like this in our script. So
let's go back and take a look at our
read contract function. And we'll just
go ahead and copy this for reference. So
under call contract functions, we'll
paste it in. Now let's go ahead and make
some variables for this. So at the top
we'll make a ERC20 address variable. I'm
going to work with USDC. So back in
Ether Scan I will copy the USDC address.
Of course if you want to interact with a
different ERC20 feel free to deviate and
then I'll replace this address with our
variable. And then let's also make one
for the ABI. So I'll do ERC20 ABI. This
will need to be an array. And then for
each function that we'll interact with,
we'll add it into this array. So the
first function that I want to read from
USDC is the name function. If I actually
go back to my browser and we look at the
ERC20 token standard, we should be able
to just use this as our human readable
AI. So I'll copy this and bring this
into our script. replace this with our
variable and then remember the function
name we are calling is name and then
instead of data I'll just do name and
then we can go ahead and console log it
and then I'll also add the address we're
reading from just like this and before
we run the script we of course we need
to call parse AI on this so we are
already importing it so we'll do
something like this we will save our
script and let's go ahead and run
And there we go. Look at that. We get
the name which is USDcoin. If we go to
ether scan, look for name and there we
go. USD coin. So you can see reading a
smart contract or an ERC20 is pretty
straightforward. Now what I would like
for you to do Now what I would like for
you to do is go ahead and call some
other functions on this ERC20. So go
ahead and get the symbol, the decimals,
and the total supply and log them. And I
would also encourage you try to get the
ERC20 balance for an account and log
their balance. And of course, don't
forget to use AI if you get stuck. So go
ahead and pause the video and I will see
you in a little bit.
All right, so welcome back. Hopefully
you have called these other functions
and if you have or if you haven't, let's
go ahead and go over it. So first things
first, we will update our AI. So we'll
just copy this. Of course, keep in mind
name and symbol both return a string
while decimals and total supply returns
a number. Let's go ahead and call these
functions. So you can see for symbol it
looks very similar to name minus the
function name is different and the
variable name is different. You might
notice since this and this is the same
we can refactor this a little bit and
that's what I'm going to do. So I'll
create a variable called token and we're
going to move this under token. And
instead of this we can just do three
dots and then token. And this is just
essentially copying whatever is here
into here. So we can do the same thing
for this. And let's also do decimals and
total supply. So this looks good. Let's
go ahead and just run our script and
make sure that this works. And it looks
like it does. We got the name, the
symbol, the decimals, and the total
supply. And so the last thing we'll want
to do is we'll just want to read the
balance of an account. Now, this one's a
bit more trickier because this will take
an input. If we look at ethereum.org,
we can see this function here, balance
of this takes in an address and then
returns us the balance. Now, we can just
copy this. We can remove this owner and
this balance. Your AI really just needs
to know the type. Since this takes in an
argument, we won't be able to just do
this, right? So if I can just copy this,
come down here and I'll say balance. In
order for us to call this, it needs to
know what account we want the balance
of. So if we go to the VM docs and we
scroll down, you can see they talk about
passing arguments. If your function
requires arguments, you can pass them
through with the args attribute. And we
can see they actually give us an example
of balance of. So we can see they add on
this args attribute which will be an
array. Balance of only takes in one
argument. So it's just going to be a
string which will represent the address.
So in our script we'll say args and
actually we'll define the address in its
own variable. So I'll just say user. Now
you can put any address you want in
here. Of course, it should be an address
that has a USDC balance so we can
actually see it in our terminal. And
what I'll do for now is just use my
developer account. I would recommend you
use your developer account here as well.
But for args, we'll just pass in user.
And then we can go ahead and log the
balance. So this looks good. Let's go
ahead and save our script and let's run
it. Yep. And it looks like we get an
error here. This should be balance of
Let's try this again. And it looks like
it was successful. You can see our
balance returned is zero. Of course,
because we used our developer account,
we probably don't have USDC on it. And
what we could do instead in our test net
for Tenderly, we'll go to the faucet and
we will fund our developer account with
some USDC. So, I'll paste in my
developer account. For token, we will
select USDC. And then for the amount,
I'll just do 100. All right. So, it
looks like our wallet now has 100 USDC.
Now, if we rerun the script, look at
that. Our balance is now updated. Now,
remember that this is in way. So, that's
why we see all of these added zeros
here. Unlike ETH, which is 18 decimals,
USDC is only six. So, that's why we have
six decimals here and then our 100. So,
this is pretty ugly to read. Let's go
ahead and make this a bit more nicer.
So, I'll actually just keep this console
log and then I'll just add a new one
with our formatted balance. So, just
like what we're used to, we're calling
format units because we are converting
from away. So, we can pass in balance
and then the number of decimals that we
want to format. So, USDC is six
decimals. So, we can do six. Now, this
will work as intended. However,
different ERC20s
can be different decimals. So, you might
have some ERC20s that are 18 decimals,
12 decimals, 9 decimals, or even six.
So, instead of hard- coding six here,
remember that we are getting the
decimals by calling this function. So
what we could do instead is say decimals
to represent how many decimals we want
to format. And we can go ahead and run
the script. And there we go. We can see
our balance has been formatted and we
have 100 USDC. All right. Now that we
have learned how to call read functions
on a smart contract, we can go ahead and
move on to our next script where we will
call a write function. So for our fifth
script here, we're going to go ahead and
send USDC to another address. We already
have some of the boiler plate here. So
we have our imports at the top. We are
importing our prompt for key helper
function. We already have some of our
variables defined. So since we'll be
performing a transaction, we will need
our URL to our test net. We have the
address of the USDC smart contract. And
then we have two functions here defined
for our ABI which is decimals and
balance of because we will read the
balances before and after our
transaction. We will need to add in one
more function which will be the function
we'll call to transfer tokens. We'll get
to that in a little bit. We define this
token object which has our address and
AI defined and we'll eventually pass
this into the transaction. We have our
code here for getting our private key
and then we set up the wallet client. We
are extending it with the public actions
and of course we are hoisting our
account and passing in the URL to our
test net. So before we start writing
code, let's go over what write functions
are. So we learned in the last script we
learned how to call these functions.
These functions were view functions. and
these view functions. If we look in
Etherscan and we're looking at any
ERC20, but we're using USDC as our
example, and we go to contract, we go to
read as proxy. These are all the
readonly functions or public view
functions that you can call on this
ERC20 contract. And all of these
functions are read only because they do
not change the state of the blockchain
or the smart contract. If we look back
at the ERC20 token standard, we can see
these functions like transfer, transfer
from, and approve. And you'll notice
that they are missing that keyword
called view. And that is because these
functions are write functions. So back
in etherscan instead of looking at read
as proxy we can look at write as proxy.
We can see a list of the write functions
for the USDC smart contract. And all of
these functions will change the state of
this contract. And we can see the
transfer function here which is
responsible for transferring tokens from
the caller. This function takes in two
arguments. an address of who will
receive the tokens and then the value of
how many tokens you are transferring. So
back at the token standard, let's go
ahead and copy this cuz we'll use this
as our AI and we'll add this on. Now we
can go ahead and take a look at the VM
docs and under contract instead of read
contract we will look at write contract
and this will be the action or function
that we'll use. We can see an example of
how they use this. And from their
example, we can actually see that it is
a twostep process. They first call this
simulate contract function on the public
client passing in the account, the
address, the AI, and the function name.
And then after this function is done,
this function will return a request
which they store in this variable. And
once they have the request then they
call write contract on the wallet client
passing in the request. And we can see
the reasoning for this right above.
While you can use write contract by
itself, it is highly recommended to pair
it with simulate contract to validate
that the contract write will execute
without errors. Now it's very important
that we do this first because if we did
not do this first and we were just to
call write contract. If for any reason
the transaction would fail here then we
would still end up paying for gas fees.
So by doing this first this will help
ensure that the smart contract we're
interacting with or the transaction
we're about to submit will not fail. And
of course we can click on this and we
can see more details on this simulate
contract function. And also note this
simulate contract belongs to the public
client and not the wallet client. So
you'll remember in our script we are
extending the public actions. So we will
be able to call simulate contract on
this wallet client. So let's go ahead
and let's copy their example here. We'll
go ahead and add it. We also do not need
to pass in the account because our
account is already hoisted. So we can
remove this. And just like the last
script, we set up this token object that
has our address and AI. So instead of
this, we can just copy token here. And
then the function name is not mint. We
will be calling transfer. Now you'll
remember that this transfer function
takes in two arguments. We need the
address of who we're going to be sending
tokens to and then a number of the
amount of tokens that we'll send. So we
can check the VM doc super quickly. If
you scroll down of course they have a
example of passing arguments here and we
can see they pass in this args attribute
which is an array very similar to how we
called balance of. So in our script
we'll do something similar and let's
define our arguments in their own
variables. So, right after the wallet
client, I'll say transaction variables,
let's define our recipients and I'll
make this the address of my second
developer account. And then let's define
the amount and we'll just transfer one
USDC. So, I'll say parse units. We'll
say one. And then we'll want to add in
the amount of decimals that we want to
format. Now, I could pass in six here,
but of course, it's better to get the
decimals onchain. So just above our
variables go ahead and get the decimals.
You should already be familiar with this
and then instead of six we can just do
decimals. So for our RX our first
argument will be the address. So we'll
say recipient and then the amount and
then the last thing we'll do we'll get
the hash of this transaction. Now the VM
docs doesn't show this in their examples
but if we scroll down we can see the
return value is a hash which is the
transaction hash. So once we have this
hash we can go ahead and console log it.
So this should be everything we need to
submit this transaction. We'll probably
want to log our USDC balances before and
after. So, what I would like you to do
is to pause the video and get the USDC
balances before and after our
transaction. And you'll want to get the
balances of both the recipient and the
account that is sending the transaction.
And then after you get the balances
before and after, go ahead and run your
script and see if it works.
So hopefully you've moved ahead of me
here and you've gone the balances before
and after and you ran the script and
made sure that it worked. If it didn't,
don't worry about it. We'll go over this
right now. So for getting the USDC
balance before where we call read
contract passing in the token our
function name which would be balance of
and then our arguments which would be
the address we're checking the balance
of we need to define sender. So let's do
that and just like we learned previously
we can call this get addresses function
on the client. And since this returns an
array we'll use square brackets here.
We'll go ahead and log our balances. The
balances we get will be in way. So we
format them. Let's go ahead and add in
our balances after. So very similar to
getting our balances before. We just
have a different variable name. And we
can go ahead and log the balances. So
let's go ahead and save our file and
let's run our script. We'll need our
private key. Oh, and we get an error
here because we used public client. We
actually need to rename this variable to
wallet client. And it looks like we get
a different issue now where looks like a
conversion issue. Cannot convert a big
value to a number. So of course we can
use chat GPT or your AI to kind of help
you out with that. But I already have an
idea of what the issue is. So for our
amount here on line 44 and we can
actually look back at the error here.
This file this 44 means line 44. So on
line 44 where we're calling parse units.
it doesn't like how we're passing in
this decimals variable. So when we use
read contract and we call decimals, this
gets returned to us as a big number or a
big int. So if you're not familiar with
JavaScript types, just as a quick
rundown here, there are different
variable types in JavaScript, right? So
you have a string, true or false, which
is boolean, right? 12, which would be a
number. And you can also have this big
int which is a number followed by an n.
And this is the big int type in
JavaScript. You can use your AI to kind
of elaborate more on the different
JavaScript types. Not covering
everything here, but in short this read
contract when we call decimals, it's
returning this as a big int. VM for
their parse units function, they are
expecting a number, not a big int. So
because decimals is a big int, we'll
need to cast or convert this to a
number. So in JavaScript, we can just
wrap this in parenthesis and then at the
beginning, we'll just say number. So
decimals, which is a big int, will get
converted to a number. So if we save our
file, our script should work now. So,
looks like we have the same issue again,
but this time with format units, which
is strange because I thought we
previously called decimals here without
having to cast that, but it looks like
we'll need to cast it anyways. So, for
format units, because we're passing in
decimals like this, just like how we
called parse units up here and we casted
it, do the same for format units. And
we'll also update down here. So now
let's go ahead and save our file. Rerun
this again. And there we go. Looks like
that worked. So our balance before was
100 USDC. Our recipient had zero. We did
the transaction which returned us this
transaction hash. And then we can see
our balances after. We went from 100 to
99. And our recipient now has one USDC.
So that completes our fifth script.
We're almost there and we're almost
done. We have one more script that we'll
do and we'll go over contract events
next. Now, our final script should be
relatively easy. For this script, we'll
talk about contract events and we will
fetch some events on the USDC smart
contract. So, first things first, what
are events? So, we'll take a look at the
ERC20 token standard. As we know, smart
contracts can have functions on them
that we can call. In the case of ERC20s,
ERC20 token contracts must have these
following functions. Now, in the case of
an ERC20, we can see some of the events
an ERC20 must have. So, we can see an
ERC20 must emit the transfer and
approval events. So when you perform a
transaction on a smart contract or you
call a write function, these functions
have the ability to emit an event. Now
this transfer event is emitted whenever
there is a token transfer that occurs.
So when you call something like transfer
or transfer from on an ERC20, that ERC20
contract will then emit this transfer
event. Now, these events just make it
easy for us to see what has happened on
the smart contract. So, if we look at
Etherscan and we're looking at the USDC
contract, we actually look at the
contract itself, we can see that there
is this events tab, we can see all the
events that are being emitted. So, we
can see transfer, approval, approval,
transfer, transfer, and so forth. So
these events just make it easy for us to
see the history of a smart contract. So
for an ERC20 or a contract like USDC, we
may want to look up some of the past
transfers or the past approvals. Now
something you'll notice when we look at
the token standard, you'll see that
these events also have variables or
parameters tied to the event. So you can
see for transfer we have a from, a to,
and a value. So, a transfer event will
show who transferred tokens, who
received tokens, and the amount of
tokens they got.
Now, you might also notice this keyword
here called indexed.
So we can see that the from and the two
are indexed parameters. And if we look
back at Etherscan, we can see this
transfer event, the from the to and the
value. And we can see the from and the
to has this indexed topic. And in short,
these indexed parameters or topics just
allow us to filter by events. So we can
see that topic one belongs to this
address. This is the from topic two
belongs to this address which is the
person who received the tokens. Since
value here is not indexed, it doesn't go
into a topic. And these topics will
allow us to filter events. So for
example, we may want all the transfer
events from a specific address and so we
can filter by the first topic. We may
also only want the transfer events for
an address that received tokens. But
since value is not an indexed variable,
we wouldn't be able to get all the
transfer events that had a certain
value. So hopefully that explains a
little bit on how events work. If you're
still unsure or unclear, you can use
your AI to help clear things up. Now,
for our script, we're going to go ahead
and focus on getting the most recent
events on the current block number. All
right, so looking at the VM docs, we're
going to go ahead and look under public
actions, and we're going to look at the
get logs function. And we can see this
returns a list of event logs. We can see
they're calling get logs on the public
client. And by doing it this way, this
will return literally all the events on
the Ethereum blockchain, which is
usually not what you'd want. And we can
see here in practice, you must use
scoping to filter for specific events.
So if we scroll down, we can see an
example of scoping. And we can see when
they call get logs, they pass in this
object with some attributes. So we can
see they pass in an address here. So
this will be the address of the contract
that you are getting the events from.
They pass in the event that they want to
get. So remember that contracts can have
multiple events just like the ERC20 has
the transfer and the approval event. So
we can see an example of them getting
the transfer event. Now just like how we
had to define an AI for reading and
writing to a smart contract, we also
have to do the same for events. So we
can see for the event they are calling
this parse AI item and then passing in
the shorthand AI for the transfer event.
Then we can see an example of them
passing args. Now remember that the from
and the to are indexed variables or
topics. So that means when we are
fetching events we can filter by the
from and the to. So if we wanted to
filter by an indexed variable, we would
pass in this args object and then the
name of the indexed variable and then
the value that we want to filter by. So
by defining these args, this will return
all the events where the from is equal
to this address and the two is equal to
this address. And then the last thing
they pass is the from and the to block.
So this function will return all the
events from this block to this block.
And if we didn't include this, then this
function would then return all of the
events from block number zero all the
way to the current block number. So
let's go ahead and copy this example. So
right under get logs, we'll paste it in.
And then let's go ahead and edit this a
little bit. So for our address, we want
to pass in the address to USDC. We do
already have this defined up here. So
we'll just need to pass in our variable.
Now thankfully the VM docs already did
the event for us. We're just going to
refactor this a little bit. So I'm going
to bring this into the variable that we
have defined up here. And then we'll
replace this with our variable name. Now
for this example, I'm not going to do
the args, but if you want to deviate,
you can. So I'm just going to delete
this. And then we'll go ahead and define
some block numbers. Of course, we could
keep this as is and this should work
totally fine. But let's go ahead and get
the current block number and then we'll
filter by that. So you should have an
idea of how to get the block number
already. We did do this in the first
script and we can see we did it right
here. So let's go ahead and copy this.
And then what we could do for the from
block, we'll say block number minus 1 n.
So from the current block number minus
one block. Now note that this n means
this number is of type big int and we
have to do it this way because the block
number we get is also a big int. So when
we do the math we want to make sure that
they're the same type. Let's also make
sure that this is public client. And now
for the two block we'll just say block
number. So, we're going to get all the
transfer events in the last block number
from the USDC smart contract. And then
we'll go ahead and add a console log so
we can see all of the events. Now, let's
go ahead and run our script. Oh, and we
get parse AI item is not defined. That
means we forgot to import it at the top.
We'll save and run this again. And there
we go. Look at that. So, we get one
transfer event. We are connected to our
Tenderly test net. So you might notice
that this event is actually the event
that was emitted when we did a transfer
in the last script. So this is actually
the transfer that we did on our test
net. So we can see the event name. We
can see our arguments are from our two
and the value that we sent the address
which is USDC. We can see the topics for
the event. So remember the from and the
two are indexed. So they are topics. So
we can see for our first topic or topic
one, this is the from and then the
second topic is the two. Now I didn't
really go over what topic zero is. But
in short, all topic zero is is a hash of
the event definition. So that's what
topic zero will always be. We can see
the data for the event which is just
this. And this data will contain our
nonindexed variables. So what we see
here is this number our amount in
hexadesimal. Then we can see the block
number. We can see the transaction hash
as well as some other stuff. So this is
pretty cool. But let's go ahead and
let's actually get the current events on
the real blockchain. So for our client,
I'm just going to remove the URL. So our
client will no longer be tied to our
tenderly test net. And by passing
nothing in here, we'll connect to a
public node. And now let's go ahead and
run this. And look at that. We get a lot
of output. And this is all the transfer
events that has happened within just
this one block number. And this is why
you might actually want to filter by
arguments if you are quering for events.
But these are all real events that has
happened on the actual Ethereum mainet.
We can actually just copy from one of
the events the transaction hash and we
could go over to ether scan and we can
search for that transaction. So if you
paste in the hash and we can see this is
a real transaction. We can see the block
number ending in 865 865. Now with this
transaction there are some other stuff
going on. So it's not just a direct
transfer. We can actually look at the
logs and we can see all of the other
events that was emitted for this
transaction and we can see one of the
logs here which is the USDC token. We
can see the from ending in 5640
which is indeed what the from is the two
ending in a8f which is indeed that and
then the value which is this number
which is also matched over here. So you
can see this is a nice way of getting
all of the events on the real Ethereum.
So that completes this script on getting
events with VM. So good job on making it
through this far. Hopefully you have
learned a little bit about VM. Maybe you
can try changing up some of the scripts
or even maybe create some of your own
scripts and mess around with some of the
functions that you see listed here in
VM. Now, if you feel like you are ready
to take your skills to the next level,
you can go over to
dapuniversity.com/bootamp
where we'll turn you into a blockchain
developer. Hopefully, you've enjoyed
this tutorial and until next time, go
out there, build some awesome stuff, and
happy coding.
🤑 Become an in-demand blockchain MASTER: https://dappuniversity.com/bootcamp CODE: https://github.com/dappuniversity/viem-examples Subscribe to this channel: https://www.youtube.com/channel/UCY0xL8V6NzzFcwzHCgB8orQ?sub_confirmation=1 Instagram: https://instagram.com/dappuniversity/ Twitter: https://twitter.com/DappUniversity Email: gregory@dappuniversity.com