foreign
[Music]
welcome to my workshop
um today we will be building an nft
bridge between ethereum and arbitrum if
you would like to code along you can
clone this repo here I don't know if the
URL is visible enough but you can get it
from the off-chain labs GitHub it's the
most recently updated thing or you can
grab it from my Twitter at these act 23.
it's the last thing I tweeted
um you're welcome to code along you're
also welcome to to listen
um uh both work most of the coding will
be so we kind of have the skeleton of
the smart contracts and we'll build out
some of the solidity directly
um and then at the end maybe we'll uh
deploy it live on the go early testnet
Wi-Fi is a bit suspicious it seems but
uh we'll see
um
so um let me talk a bit about arbridge
room first um so Arboretum is an
ethereum layer too uh for our purposes
we can think of arbitrum as its own
blockchain because it is a blockchain
um runs on top of ethereum and kind of
inherits security from ethereum
the uh the important thing for for
building this bridge is arbitrum allowed
a sort of arbitrary message passing
between layers so we can send
um I'm just going to leave the Twitter
there for a bit so people can find the
link
um
um we can send arbitrary messages sort
of from L1 from an L1 contract directly
to Layer Two uh I shouldn't say directly
maybe sort of asynchronously we can send
messages to layer two but arbitrarily
um and likewise we can send arbitrary
make arbitrary contract calls from layer
2 down to layer one
and just sort of using those tools that
Arboretum gives we can build kind of an
nft Bridge around them and that's what
uh that's what we'll be doing
um
so the
um just to talk a bit about the
architecture of the bridge itself
there's obviously many ways to build uh
an area c721 Bridge or something like it
this will be very similar to the
architecture we use currently on mainnet
speaking of mainnet just to clarify this
disclaimer here this is not tested code
not audited don't to playlist on mainnet
not liable whatever okay
um
code that actually is on man that um we
use similar architecture for what we
call our custom erc20 Bridge you can
read more about that in our docs this
will be a similar version of that
um by custom what we mean is the token
contracts themselves on both L1 and L2
are sort of deployed directly by you
know a developer a user so
um they'll be
an ERC 171 contract on L1 another on L2
and as long as they have certain
affordances they'll be able to sort of
use this bridge uh kind of interact with
this bridge
um the basic functionality that we want
from a bridge we can we should just say
clearly in case it's not obvious
um is basically we have we're sort of
going from L1 so the L1 contract our our
721 will be the source of Truth
we want to be able to
um essentially use this uh the 721 on L1
and get a representation of a token on
L2 that effectively gives us rights to
the Token back down on L1 so you can
deposit it and you're guaranteed rights
to withdraw it
um the the contracts involved will be
what we call our Gateway contract so one
contract on L1 one contract on L2 these
are the sort of bridge contracts that
are in charge of managing the mapping
between the L1 token address and the L2
token address as well as handling the
deposit and withdrawal functionality so
I'll just sort of walk through the uh
the flow real quick and then we'll get
started filling out the code
um but um so again the flow is we we
have our we have our token contracts
deployed on layer one and Layer Two
um we'll see what affordances they need
as we go
um but there's kind of Three cross chain
messages that are going to have to
happen here so one is we the um the L1
token contract kind of has to register
itself to the Gateway which basically
just means it it kind of opts in to what
its L2 address will be or what its
corresponding L2 token address will be
and we'll do that we'll just sort of
store a mapping on the L1 side and the
L2 aside that Maps L1 token to L to a
token so that's this kind of
registration so it needs to be able to
make that call and that'll be a
cross-chain messages so both both layers
sort of know what token maps to wear uh
we need to be able to deposit deposit in
this case is going to mean sort of
sending the token to L1 and escrow
sending a cross-train message to L2 so
our corresponding contract mints so
escrow and mint is a deposit and a
withdraw is kind of the opposite um
we're going to burn the token on L2 send
a cross chain message down to layer one
which will eventually release it from
SQL
um that's the flow we need if you're
coding along uh yeah you can get clone
this and yeah let me get to deploying
you can just set these environmental
variables
um you'll need
an L1 gorilla RPC URL
um and some uh private key funded with
Gorillaz if you want to actually deploy
it but um uh yeah
for now let's let's let's dive in I'll
get rid of
my Twitter
um
okay
so
um to start we can sort of go so we're
going to start with as far as the cross
chain messages go we'll start with this
registration step
um so let me do this
um so first of all just looking in our
repo real quick
um I'm just in the contracts directory
here's our contracts that'll be deployed
on ethereum here's the ones that will be
deployed on arbitrum L2 and basically we
have the L1 token itself dl2 token
itself scale one Gateway the L2 to get
where we'll talk about this Alias helper
later just ignore that for now
yes
is that good
mark
a lot more yeah
um
so
we'll start with
so this is the token contract itself or
an example of the token contract itself
that we might want to use that we might
want to bridge right we can see
um this is already filled out so nothing
to do here
um we can see this is you know standard
open Zep line Dr c721
um we're just giving ourselves the
ability to Minge so we can mint stuff
and play with it
um the important thing here the
important affordance that it needs is it
has this registration method so this
will actually make an external call to
our Gateway and again this will update
these mappings on L1 and L2 okay so
that's already done the token has to be
able to register itself
um so let's start with that that actual
registration
um so if we go to L1 nft Gateway
so again this is like the Gateway
contract the bridge contract
um
so the key thing that we wanna that
we're going to be sort of doing here is
updating this mapping on the L1 side and
then similarly there's a mapping on the
L2 side that will catch with a cross
chain message
um while we're here so basically we just
have a bunch of functions we need to
fill out but we can just look at what
we're initializing it with
um so this is counterpart L2 Gateway the
L2 side of the actual nft Bridge we're
storing that and then inbox and Bridge
these are both arbitrum core protocol
things that we'll use for cross-chain
messaging uh that we'll get to in a
second
um and then yeah this is this is the
mapping we're actually updating let's
just peek at the L2 side as well so the
L2 Gateway
um
similarly we have an L1 to L2 token
mapping that's what we're targeting as
we update and uh yeah we'll talk about
that other stuff
um
when it comes up okay so let's um let's
start with this registration step so the
token itself made this call right
um to register itself and we want to
update the mapping basically so let's
um let's go for it
do okay
um
so we are going to kind of assume that
the caller is a token if it's not that's
fine it won't affect anything else
um
so we'll call this L1 token then just
grab the address because we're going to
use this in a few places
hopefully my hands get slightly less
shaky in a second
um
so there's the L1 token address that
made this call and now we just update
our mapping
um there'll be a check we want to do but
let's just let's just get this code in
first
L1 to LG token okay the L1 token mapped
to
the L2 address provided
cool
that was easy
um now
um one question we should ask here is
should you be able should the token be
able to sort of re-register to a new
address
and we can decide you know for the sake
of Simplicity let's say no this is kind
of by the way this this whole
architecture is meant to be as simple as
possible kind of minimum viable token
Bridge so we can kind of focus on the
cross chain messaging stuff and
understand what's going on so if the
Gateway you know if the token's allowed
to sort of change itself to address that
could make our assumptions kind of weird
um but another thing we might want to
ask is can it re-register itself to the
Sam address and we actually do want that
and I'll explain why but first let me
let me write this check so um
if the um
so we're going to check
um
um actually let's do this yeah
so before we register it let's grab
we'll call it like current L2 address
and we'll grab it from the mapping
so before you registered it here's what
the address was
yes
and we're going to say if it's all we're
going to check to see if it's already
registered
um
something I'll figure that out um check
and see if it's already registered
Excuse me yes I have a more high level
abstract question here but specifically
here are you require for nft smart
contract to um be compatible with your
Bridge fundamentally because they need
to self-register right yes and if I'm an
nft publisher why do I need a bridge I
can just click create a button on my
website like burn photo can and then
give a tokens when you buy no Bridge
needed uh you could in that case it'll
be trusted right so this we want it yeah
but nft fundamentally our tokenization
of Rights so fundamentally nft are
trusted well if we want the bridging
process itself to be trustless let's say
right so correct let's put it that way
right so the bridging so yes whatever
trust assumptions are within well if
that's as last as for cross-pressession
yes exactly so again we're treating the
L1 contract as the source assuming yeah
it's a good question so L1 contract is
source of truth whatever trust
assumptions it has we're sticking with
but the bridge itself the bridging
processes compatible with a legacy
contract it's only for upgradable
contracts or new contracts specifically
built for interact with this bridge
that's yeah that's right if you have an
old NFTA for example what you could do
is deploy a new contract that's that's
kind of a wrapper and use that one yes
exactly
okay so in in terms of in terms of
re-registering so if it's already
registered we're going to actually allow
it to register again as long as it's
registering to the same address
um as strange as that might sound
um so we're going to acquire that the
current address is equal to the new one
and the reason we want to do this as
we'll see is we're going to be sending a
cross chain message
um
I'll just keep shouting okay um let me
know
um
the okay so we need to know the address
that um at which we want to trigger a
message on the L2 side which is going to
be our L2 nft Gateway which we have
stored as counterpart Gateway and then
we also need to encode the actual
message we're calling right so on the L2
side just peeking ahead the message that
we're going to be calling
just because we have the name here
finalize registration from L2 we need to
sort of encode that and specify that
that's the that's the method we want to
call so let's uh let's do that and
encode that data and we're going to
actually just do that in a separate
method here get register L2 message call
data so here we're going to encoding the
method of the call that's going to be
made on L2
um
so
we'll use
[Music]
um
encode with selector
right so this is just this is you know
um
um this is what the origin cross chain
protocol expects we encode the actual uh
the function name itself we've already
imported this thing so it should be yeah
L2 then if T get away
um
um what was it called finalized
registration from the one that's the
method we want to call
so there's our function
um
and we pass in its parameters which are
just going to be
these so basically these are the things
that we need at L2 to do the second half
of the operation which is updating the
mapping at L2
um cool
so that gets us the call data there's a
reason I'm putting this in its own
function this is going to help us
estimate gas later basically but for now
if that's not satisfying we can just say
it looks nice in its own function easier
to read okay
um so that'll be the call data for our
cross chain message call let's grab that
in our registration method
um
yeah
and uh we pass in what did we call it L1
token
L2 address
uh let's call this something L2 message
call data
cool
um
okay let's just like see if this
compiles
um just to make sure we don't go off the
rails cool okay
um
okay so now we have our call data now
with that we can actually send a
crosstain message so I'll just briefly
show you
um the interface for sending L1 to L2
messages so this isn't in the in the
repo this is um in the arbitrim uh
uh the Arboretum core protocol Nitro
Reaper but just to show you the
interface here in fact let's do this
that's like reasonably visible kind of
um
um here's all the things we need
um create retryable ticket is how we
send an l12 message
um let's just let's start filling out
the fields and then we'll talk about
um all these new ones
but for now okay so um
we have our inbox address saved again
this is just core protocol
um
create ah it's Auto filling everything
um
I don't know why it's doing that
okay
um
okay so two is uh the address at L2 at
which we want to execute the L2 call so
this is our other Gateway counter Portal
2 Gateway maybe I can do something
as there's more it should
um become horizontal
um or vertical rather the okay so we
have that
um again this key thing down here this
is the actual message that we encoded so
we have that now everything else has to
do with kind of paying for gas at all
too so the way these retrieable tickets
work is we not only specify a contract
call we also fund it
so we literally you know we're
effectively depositing ether
um along with this message we're going
to use that ether at L2 to do the
execution so a lot of these other
basically all these other fields have to
do with what's going on in that L2
execution
um so um L2 call Value that's easier
because that's just zero in this case
we're just updating a mapping we don't
need call Value on the L2 side
um and now we have all this stuff that
has to do with gas
um so I've just included those in this
L2 gas param struct let's just look up
at that
but basically L2 execution so max gas
and gas price bid this is very similar
basically the same idea as gas price and
gas limit on L1 right works the same way
on arbitrum the one additional thing
because this is a cross chain messages
there's this special Max submission cost
thing an additional fee for processing
cross chain messages because there's
just a bit it's a bit more resource
intensive so that's like proportional to
the amount of call data that you use but
for intents and purposes here we're just
we're asking the client to provide all
these values so we don't have to worry
about where they came from
um but those are the those are the
fields we'll need to to create a cross
chain message
um
and I should just do this now as well
um like I said we're sort of depositing
The Ether along with it
um
so we're going to send that along
um is that right
um that looked weird to me but I think
that's right so we send this along this
will pay for the L2 execution
the L2 will sort of automatically try to
execute it if it fails the execute on
the L2 side it can be retried for a week
that's why it's called a retrievable
ticket and this gives us kind of an
atomicity guarantee
um anyway let's keep filling these out
so we have the O2 counter bracket with
no call Value and now we get to these L2
gas params
um
so that was just passed in
can I make this
uh
I feel it's getting weird I'll just type
it up
um
so max submission cost is next
um okay now we see over here we have
these refund addresses and this is
basically on the L2 side if you provide
excess gas in your deposit you specify
which address you want that guys to end
up on and this is necessary basically
because it's a cross chain message we
need to sort of do something with this
excess gas uh yeah question
they also call Value
yeah
yeah so I think of L2 call Value when
this cross chain message we're creating
this effect something that's going to
look a lot like an L2 transaction it's
just going to appear at L2 unsigned
basically okay so for that L2
transaction we need to specify kind of
all the usual fields okay so but the
we'll we'll sort of build out the
contract call in a second and we'll see
what the L2 transaction is doing but
here we're specifying all the necessary
things so we have like the contract call
the address and then yeah the actual
call Value just on the L2 side
um
yeah hopefully that makes sense if not
hopefully it'll become clear in a second
when we see the full the full flow
um
yeah
um okay so the refund address
um
we can just we're asking the client to
provide
so in this cross chain message sort of
where your excess ether gets refunded at
L2
um
and then there's a second refund address
which is if there is call Value you
specify if the L2 message never succeeds
you specify where that call Value gets
refunded at all too in this case doesn't
really matter because there's no call
values we'll just use the same the same
refund address and now we have L2 gas
params
uh hard to see
what's it called gas
Max gas
so that's kind of the gas limit
and then L2 gas params
um it should be something like what do
we call it
Max fee for gas I think
for gas price there we go gas price bid
okay
um
let's get rid of this hoping this will
just horizontally
um okay and then finally the last
parameter was the actual encoded uh
the encoded call data of the L2 message
there we go okay
um we are complaining
yes I did mean that
okay looks good
um so there's a question message
um
this gets published and this will sort
of automatically execute this method
that we encoded at L2 so let's go to
that now
um which if we remember was this thing
finalized registration from L1
so this gets automatically executed now
we want to finalize the registration
which means basically the same thing it
did before we're just updating this
other mapping okay so that both sides of
our Gateway know uh what token goes to
what
um so we'll update the mapping l12 token
the L1 address that I got from the cross
chain message uh gets updated with this
L2 address
um and that's basically all we need to
do there
um
now uh other than one other important
thing which is we want to make sure that
this method at L2 can only be called as
a cross chain message otherwise this
whole thing breaks
um so we'll add this only counterpart
Gateway modifier
and now we have to fill that in
I think I still have to do here no more
to do um
so only count up our Gateway this is
um
gonna be a simple required check just
with a slight gotcha
so here we're going to ensure that the
message.cender equals this thing that
we're calling counterpart Gateway L1 or
counterpart L1 Gateway aliased
so notice that we initialize the
contract with the L1 Gateway and then we
do this thing where we Alias it
um I'll just just to peek at this real
quick
the actual aliasing step you'll see is
just like adding to the address itself
some constant offset so what's going on
here
is um
when it comes to cross chain messages
the way that the sender address appears
is not the sender of the L1 contract and
this is basically so that on the
contract level we can easily
differentiate between a con messages
coming from L1 versus messages coming
directly from L2 we need to do this
because arbitrim contract addresses at
Arboretum are computed the same way they
are
um the same way they are on ethereum so
we can and often do have two different
contracts you know one at L1 one at L2
that share the same address so if you
can't differentiate between them you
could get all sorts of weird spoofing
attacks in this case every cross chain
message kind of lives in its own address
space this Alias space so we know this
is definitely coming from the L1 Gateway
um cool there's our registration step
um
um yeah any questions there because
that's that's kind of yeah
if you didn't yeah so if we didn't do
the like if aliasing wasn't part of the
arbitrine protocol for example if the
message.sender was just the address from
L1 then the uh the deployers of this
contract let's say if they were
malicious they could deploy something
else at L2 at the same address
that and then you know it becomes very
hard to reason about the risks there
yeah
um so this way it's protecting you from
malicious developers basically which is
a good thing to protect you from
anything else
cool okay so there's our first there's
our first cross chain message just
simply doing the registration
um let's do the deposit so this will
also be an L1 tile two message
um and uh it'll look fairly similar
actually let me just make sure this
compiles again because I'm not using
remix and I'm paranoid okay
um I'm just running just compiling with
hard hat um
cool okay so our L1 token registered so
now we're going to assume or we're not
going to assume anything but if this
token is indeed registered it can do a
deposit
um let me just
make sure I didn't forget anything cool
okay so um now we're gonna deposit so
you can see it as once again the L1
contract is the source of truth that's
what we use so that's what's passed in
as a parameter
um so we'll use that to look up the L2
address uh first
so let's say L2 address
equals uh we'll look it up in the
mapping
but we just saw the registration step go
down and
yes
um and now we're just going to make sure
it's actually registered
um
so just require that this thing is
really an address
L2 address
um
register okay
um
so cool if our token is registered our
L1 token opt-in we inherit whatever
assumptions Helen token is making again
um now we can proceed so we can actually
do our our um our deposit so deposit
again and tells these two things we're
going to Escrow the token here send the
token to this contract
um yeah
we have our 721 721 interface imported
um
so we'll uh connect to it at the L1
token address
and do save transfer from
uh from two token ID yeah so from the uh
from the sender
the caller will deposit
to this contract this is the escrow
so here
here
um
and the token ID
yes
cool so there's the first half of our
deposit
and now very similarly we're going to
send a cross train message to L2
um again we're going to need so we have
similar parameters here the gas Brands
the refund address all the sort of gas
specific stuff once again we need to
encode the call that we're going to make
on L2 which we will do in a function
here
um so this is basically again just think
of this as like the information that we
need to send to L2 and the actual
function we're going to be triggering on
L2
is called finalized deposit and there it
is okay so let's encode the call to that
now again as a separate function
um
code selector
awkward autofill
um
so just like before
awkward autofill
um and it's the same parameters in the
same order uh so yeah function selector
here
and it's just yeah all these
so this gets encoded
um
yes
now let's grab that
um
so we have our uh what I call it before
a little two message call data or
something
in fact I want to call it the same thing
so I can be lazy later I'll do a message
called Data yes
um
and we pass in the parameters so this
was L1 token
L2 address
token ID
and two two is the sort of um
the address at which we'll be depositing
the token the sort of receiver of the
token at L2
um
okay
cool so we have our call data and now um
once again we're going to send a sender
a tribal ticket cross-chain message in
fact in the interest of time and being
lazy I'm just going to copy and paste
this exact thing because it should be
all the same parameters I believe
I don't see why not
um so we created a travel ticket yeah
Sam browners always love the same L2 gas
business once again the L2 call Value
we're not sending any ether uh or rather
we're not you know using any ether in
the um in the L2 side of the message we
could if we wanted to have the option of
like deposit your token and some ether
we could add a parameter there but we're
not
um and then yeah the only difference
from before is we just
calling a different method um which we
did here
um cool so now we will finalize our
deposit and finalizing deposit means
minting at L2 so let's do that
um
so now we're back on the L2 side
um and cool we want to check here that
it's properly registered
um
there is something that looks like an
edge case which I can talk about in a
second why it's not really a scary Edge
case in terms of the timing of
registration and things like that
um
what am I doing
um yeah so we're going to look up
in our mapping
uh yes and see that that matches with
the token provided
so this ensures that it's registered on
the L2 side
yep
um
and cool that should be it um once again
we'll add our only counterpart L1
Gateway that's kind of the important
thing so it's gonna only be called via
that cross chain message
um
and okay now we can do the L2 part
um the L2 part of a deposit is minting
so now we now we want to actually look
at our L2 token which we haven't even
looked at yet
um what is going to be deployed on the
L2 side
so here I'm
this was you know not this was deployed
directly and basically this is the
Assumption this is the assumed interface
it has to have these methods here yes I
have a method that allows the bridge to
Mint and allows the bridge to burn uh
and we can just fill that out now
um fairly simple
mint
okay so we expose the ability for only
the uh only the Gateway or the L2 the L2
bridge to Mint
um I think it's in that same order I'm
not mistaken I think that's how that
works
um
and let's add this only Gateway that
should be probably be clear if it was
called like only L2 Gateway or something
but when we initialize our L2 token we
give it the address of the LG Gateway
cool so now that's exposed dl2 get the
L2 Gateway can uh convince so let's uh
let's mention
um where are we finalize deposit yes
so let's
uh connect to our L2 token
um
or rather
[Music]
um
yes let's connect to the interface but
we'll use our assumed interface
um L2 our vrc20 which has the bridge
mint and Bridge burn functionality
uh yes I'm sorry
the L1 and L2 Gateway are they depleted
it doesn't matter yeah it doesn't matter
they don't need to be yeah so just as
long as we as basically as long as when
we like when we initialize these
contracts we know they know each other's
addresses we're fine so there's no need
for them it's probably better than not
at the same address because that tends
to confuse people we've had issues
actually
um
um yeah but um so they can be if you're
if you if you want to be fancy with how
you'd apply them but they don't need to
be
um
okay
um what are we doing we're minting
yes
and my editor is smart I think
it's not the right order
yes cool
um
so there's our actual mint
um
and that's it
right because again this this can only
be called as a cross chain message so
there's uh there's our deposit yeah
any other questions before we move on to
the last
um time is fine so let me let me
slightly speed around this but um
um let's um let's do a withdrawal
so here we're going to
[Music]
um
need a cross chain message this time
from L2 to L1 I can just show you
the arbitrine pre-compile we're going to
use which is called our obsessed so
every Every arbitrum Chain gets this
pre-compile it lives at um address 100
or that hex
um and this just exposes all sorts of
system level functionality for our
purposes we have this send transaction
to L1 very similar idea right we specify
the L1 address that we want to make some
contract call and we give this this data
this call data which is the encoded
message that we're actually going to
call
notice in this case there's none of that
gas stuff
um and this is because essentially
there's no such thing as automatic
execution on L1 so this has to just
happen in two steps right we initiated
on L2 and then sometime later the client
will sort of finalize it on L1 via its
own transaction so perhaps slightly more
inconvenient for the user but convenient
for us now we don't have to have those
parameters so it's a two-step process
um and this is where you have to wait
the optimistic roll-up delay period if
you're familiar with that stuff
um okay cool
um so let's do our let's do a withdrawal
here
um
yeah let's go for it
so let's see we have like 15 minutes
that should be fine
um let's check to see so we're
initiating this on the L2 side once
again we we have our mapping that is the
source of truth of registration
so let's uh check to see that the thing
is registered and also get the L2
address
so again we're specifying our token by
the L1 address that's always the source
of Truth
um
and here I think we just need to see
that it is registered
yes
um just like before
looking for how to address require that
it exists
plus autofill
um
okay
um
so now okay for a withdrawal
we want to burn first
um so let's fill that in back in the
token we want to expose the affordance
for the gateway to burn
um so let's do that in this case we also
need to actually verify that you're uh
that you know this owner has the right
to burn it which we didn't have to do
for mint
um basically in order to get to the mint
code path you had to transfer a token to
begin with so there we were safe that
you were the owner in this case you just
gave me a token ID who the hell are you
um you better be the owner so let's make
sure you're the owner
um so the Gateway will be responsible
for passing in the owner address
and now we want to I think it's owner of
I'm blanking out that's how I understand
um
I think that's right
um
what's that
is that right yeah thank you
um
so we want to acquire require that it's
the owner passed in let's call this like
whatever it's like potential owner
should be owner whatever
um yes
cool so we make sure that this so yeah
again the Gateway is responsible for
making sure that that owner address is
passed in we make sure you're the owner
um and then okay once we are we can we
can go ahead and burn
yes maybe that's a nicer way to do that
but that works
um I don't think there is
um
okay so now we've exposed Bridge burnt
oh this also has to be on the Gateway
of course would have been critical
vulnerability
um
okay so now we can burn
um
yeah
so we connect to the Token on the L2
side of the address we just got from the
registry we call Bridge burn we pass in
the owner or claimed owner which will
just be the caller of this method
um and the token ID
does that work yeah
yeah
okay
um cool so we burn on the L2 side now we
want to send a cross chain message down
to layer one
um which once it reaches layer one we'll
release the same token from escrow same
token we're just notice I didn't say
this explicitly but we're just sort of
tracking tokens by ideas we're just
keeping the same idea two as it has on
L1 because why not
um okay so we need the destination
address which we have the L1 counterpart
Gateway
um let's do what we did before and
encode
um a function call here because we'll
just do it in line not as a new method
okay so what's our function and what do
we need to send is the question in order
to sort of release it from escrow on the
L1 side
um we can speak
so our method here is called finalize
withdrawal we specify these things
um so let's encode that on the L2 side
um I forgot them already
we specify oh and token token ID in the
two address okay
and it's called
finalize withdrawal
um
um yeah uh Owen token
e oh and let's call it something so this
would be the L1 message call data in
this case
um that doesn't oh selector yes
this will be encode it with the function
selector on my linter just be nice it
will not oh well
um okay so this is the message we're
encoding to send to L1 and now
um
there we go is that compiling
yes
um
now we will uh yeah we'll send a message
to L1 with the arbsys pre-compile yes
yes
Yeah so basically that's a good question
so the question between here was like
how do we ensure that this mapping is
legit essentially right like how do we
know this isn't the actual mapping and
basically that's the registration step
ensures that so we're we're inheriting
whatever trust we give to the L1
contract only the L1 contract itself can
register
so the L1 contract the token contract
itself is the only thing that can
self-register and it specifies the L2
contract so if it lies it lies but
that's
right then that means that the elwind
contract was malicious or if it gives
the wrong address that's its fault okay
so we're assuming that this L1 contract
over here when it makes this
registration call
picks the right address on the L2 side
right I guess yeah I don't know if that
really answers your question but I think
maybe the key thing is only this
register call it can update the mapping
and only the token itself can affect it
um
um I hope that's helped
um
okay where are we here oh yeah now we
have to send our send our actual message
so let's do that here's our message
um we have this Arab system by the way
I'm just you know in the interest of
time not showing but all this stuff is
imported these are some uh this is like
our library
um where we publish these interfaces but
anyway
um so again it's just a pre-compile at
address 100.
and our method that we're using is ntx
to layer one
um the address is the Gateway contract
counterpart Gateway or whatever it's
called counterpart L1 Gateway
not the earliest version this time we
want the actual address
um and we'll send the call data
um
what am I doing
oh address sorry
there we go address 100. and that looks
right to me
yes
um I think that's all we need to do here
I think I missed a return statement or
two earlier
um
cool
um okay so let's uh finalize that
withdrawal
um this part should be fairly simple
um
so what does it mean to finalize the
withdrawal this means just release it
from escrow
um well okay so the first part will be
simple
um let's release it from escrow so
transfer it out to the
uh the destination address or the uh the
two address I should say
so we connect
um
and we transfer
um
from two token ID so from here
right it should already be escrowed
sorry
um too much coffee very jittery
to
um and token ID so there's the actual
um
there's the actual method that gets
called now uh the last thing to do
um is
uh uh fix this error
oh I did something stupid
um
there okay
um the last thing to do is to make sure
that this actually only gets executed as
a cross chain message
um so this is on the counterpart L2
Gateway this is a bit more involved at
least to understand the pieces of it and
I see there's eight minutes left so
maybe I'd rather if there's any other
questions feel feel free I feel like
maybe that's but
if I don't see it just shout yeah
yeah the doodle contract itself will do
the registration and it only has to kind
of register once is the way to think
about it right so that so once the
contract itself and it's not it's not
necessarily the owner of the contract I
don't think of it that way it's just
however the contract is set up the
contract needs to be able to register so
in this case it's not the owner um in
fact in this case anyone can call
register token Dell to which means in
this case actually it's probably not the
best idea you have you should like
deploy this
contract and register right away to the
right address but as long as some as
long as the function can make the
external call to register
they can register and whether that's you
know whether that's proper whether
that's malicious you should look at the
L1 token contract so that's yeah
um probably the doodle team right so
yeah so who like whoever so somebody
basically like whoever sets this up
right like we're probably the same party
that deployed the L1 side
um we'll sort of plan this
um so they had to play both and you know
the reason you want you wanted to play
both is so you just know which address
to set okay but the other one but the
other one contract ultimately has has
control
we said
yeah
no so good question in the happy case in
the common case no the question was when
we when we deposit yeah
um does anyone need to call a function
on L2 so the way these retryable tickets
work is already Richmond cross chain
messages is it should it'll execute
automatically it'll try to execute
automatically so we Supply the gas for
the L2 side to execute
um and assuming that doesn't revert the
user doesn't have to do anything else so
from a user perspective they publish an
L1 transaction they wait a few minutes
and then it just it'll get minted on L2
now we can't guarantee the execution
will succeed at L2 like maybe there's a
gas spike in the time between when you
deposited and when there were tribal
tickets created at L2 if it fails then
someone's gonna have to kind of poke it
again to re to re to retry it to
re-execute it be able to wake to do that
but normally yeah in the happy case that
shouldn't happen
correct
correct we Supply it Supply it here so
actually so when we do this create
retrievable what this is doing create
retrievable ticket behind the scenes is
this actually depositing this
message.value ether okay
so this is so that's that's that's
happening behind the scenes so this so
this this value that we send
is actually getting deposited to L2 and
that has to cover all of this gas cost
that we're supplying so that has to
cover
you know uh uh you know gas price times
gas limit plus Max emission Cost Plus
call Value
yes
correct the L1 yes the L1 call supplies
the gas for L2 now by the way as like a
side note if you already do have gas at
your L2 address there's ways you can you
can use that but this is this is the
path that we encourage for doing
cross-chain messages is just Supply the
user directly for various reasons yeah
um
um yeah cool then you can if you look at
our docs you know I I brushed over some
of the details of retrieable tickets
because
um yeah there's a lot going on there but
that's that hopefully gives you the gist
um any other questions
I'll just start writing out how we do
this in the meantime
okay might as well just finish this
because it's
um so here's how we're actually
guaranteeing that
the finalization of the withdrawal is
really a cross-chain message and here
this gets a bit into the weeds of how
the sort of cross chain messaging
contracts work in arbitrum but basically
cross chain messages all come from this
bridge contract which is a protocol
contract
um I'd like to show it to you if there's
time
um maybe I'll just show you the first
part
um that's that's the ultimate caller let
me show you the flow why not
so crosstain messages just real quick
um
if we look at how they're executed so
they're initiated on L2 as we saw in the
case of you know you're gonna have to
wait for the dispute period because it's
an optimistic grow up
um and then anyone can call this execute
transaction thing so in this case if
that's not automatic the way where
tribal tickets are there actually is the
second L1 step
um the thing I want to show you in this
execution
here
here what we do is
we um
as we're executing this cross chain
message we save this context thing so we
can expose all sorts of data about this
what's happening you know what happened
from L2 notably this sender data is what
we're going to need so we expose that in
something like element L2 sender
but during execution it's a little hard
to show I guess but during execution the
point is as we're executing a
cross-chain message we can access this
context so we can do the checks that we
need
um
so that's going to be important that's
how we'll ensure that it's really coming
from the L2 Gateway
and then finally
um the thing that actually makes the
call is going to be this other contract
that's the bridge so that's always how
L2 to L1 messages work so we ensure that
it's really coming from the bridge and
then we would also ensure I'm sure it's
really coming from the bridge that's
half the step half of the work the other
half is making sure that it's uh we grab
that context and make sure it's really
coming from
um the address that we expect
um let me just throw that in here I
think there's time
um so that this is at least complete
um so we'll grab that out box contract
and this is uh we can just do this in
one line I guess slightly ugly but we
can instantiate the bridge
and then the bridge gives us access to
the active outbox this is just
so we don't have to store another uh
another address
um
yes
um
that looks like something might be off I
don't answer
uh attention this is hard to do live
um
I didn't call it anything yes
thank you
and then I think that's also messed it
up
um
yeah
I'm gonna sing a
oh at the end yeah that should be
yeah we're instantiating after we call
there we go that's the address okay so
we're just grabbing the outbox address
which is stored in our protocol
contracts
um and now uh we can get the L2 sender
via this neat little context trick
um what is uh L2 element Center
how to to L1 sender so that updates
during the context of execution and now
we want to make sure that that thing
um is the counterpart gateway address
basically
uh
from Gateway
okay
um so because there's no automatic
execution because this doesn't work the
way you know tribal tickets work
checking out the sender is where we
expect it to be is slightly different
but this tells us yeah it's from the
address we expect and it really is a
cross-trained message
um cool since we're almost out of time
you know so there's if you're coding
along or if you want to grab the repo
there's a branch that has like a
completed version I can also push this
one but there's um scripts to deploy the
contracts I can even just
really quickly run then
um
I don't think there's anything much to
show there but then there's another
script that'll actually do a
registration and deposit so you can mess
with it I'm gonna wrap it up okay
um
but uh yeah cool if we have to stop
there I guess we'll stop there yeah
cool uh thank you all for listening