Connext 🛠How to Build a Crosschain App - Eddie Ye
ETHGlobal·Mon, Oct 9, 2023, 12:00 AM
Join Eddie Ye from Connext for a talk titled "How to Build a Crosschain App". As new users flock to web3, critical infrastructure advancements are needed to scale. Scaling Ethereum is your chance to shape the future of Ethereum. Join us March 10th-29th for three weeks of hacking and summits featuring teams like Gnosis Chain, Scroll, Hyperlane, UMA, IPFS/Filecoin, and more. ----------------- Follow us: https://twitter.com/ETHGlobal​ Check us out: https://ethglobal.co​m Email us at: hello@ethglobal.com
Transcript
foreign [Applause] [Music] to the scaling ethereum workshop for connects I'm Eddie an integration engineer at connect and I'll be walking through how you can use connects to build a cross-chain application so we're going to start with the docs site here at docs.connects.network and at the bottom of the home page you'll find a bunch of helpful links including a link to our Bridge UI and the Explorer site but right now we're just going to jump into the concepts to do a quick review of what connects is so connect is a modular protocol for securely passing funds and data between chains what this means is that you can not only send assets between chains but you can also send any kind of arbitrary data such as data that specifies a call to a function on another chain and what we're going to be doing in a few minutes here is walking through how to build a cross chain app or zap as we call them one thing I want to touch on in relation to modularity is that we've built connects as a system which derives its security from ethereum and it plugs into these canonical messaging bridges of each chain so connects is actually able to shortcut the normal waiting time for messages to be passed between chains by aggregating all the Merkel Roots generated on each chain passing them to ethereum and then verifying their correctness off chain this kind of faster optimistic layer expedites data passing as long as no fraudulent transactions are detected by monitoring agents or Watchers otherwise the system will fall back to the full waiting time for the chains canonical Bridge and this mechanism gives you the best trust guarantees um because we ultimately defer to the security of the underlying Bridge used by the chain itself and we just have this fast optimistic layer on top that's secured by a one of n liveness requirement meaning only one honest Watcher needs to be online at any time to fully secure the network and that's not really a difficult requirement in fact anybody can run a watcher so if you're really skeptical then you have a trustless way to guarantee the library's requirement of the network now if you want to get a deeper understanding of the architecture and bridges in general I invite you to take a look at this page look through the rest of the pages and docs here but for this Workshop we will focus solely on how to actually build something and connects so we're going to jump into the developer section here and as a general outline of what we'll touch on today um we're gonna look through a few examples of cross chain applications using connects and then we will talk about a couple of critical Concepts that are important to understand as a zap Builder so um the quick start guide will actually walk you through step by step how to build a cross chain greeter it basically assumes that you have nothing set up so it'll show you how to install all the prereqs and how to use hard hat which is a smart contract development toolkit to deploy the contracts that you can just copy and paste for this page but for this Workshop we'll take a slightly different approach by cloning this zap starter kit that we've built this is just a GitHub repository that contains all the examples and docs and it will be slightly more favorable for people who just want to have a running start with something that works out of the box so um let's open this up and and take a look at what we're working with here uh to build this app you are going to be calling a single function exposed by connect um on any of our supported chains and that function is called xcall and we will see very soon what kind of parameters actually go into this pretty shortly but in general you are going to be sending either assets or data to be used for a cross chain function call or maybe a combination of the two but if you're sending data there is a distinction to be made between unauthenticated and authenticated calls which we will talk about later um and the call Flow between the two two chains that you're interacting with is going to look something like this where you have some origin chain and some destination chain you're going to be writing a source contract that's deployed to the origin chain and that contract will call xcall uh sending in the appropriate parameters and then connect system will basically route that data in any sort of like asset transfer to the destination chain if you happen to write a Target contract and deploy that to the destination chain you can specify that Target contract as the um as the as a Target address that connects will call and um connects will basically be able to unpack any call data that you send in with the x-call and use that to call a specific function on your Target contract in some cases uh this you don't actually need to deploy a Target contract so if you're just doing something like sending assets across chains you really only need to build one a contract on the origin side and we have an example of that called the simple Bridge which is just transferring tokens from a user on the origin side to some specified address on the destination side and since there's no actual function call that's needed to be called on destination you don't have to deploy a Target contract but for today we are going to jump into a greeter example and this greeter is going to do both an acid transfer and an unauthenticated function call so what I mean by an authenticated call here is that um the Target contract has a function that we want to call but it's not permissioned in any sense so it's not looking for um some kind of owner it's basically just completely open for anybody to call so the context for this example is we have a destination greeter contract on the destination chain it stores a greeting variable that we want to update and the update reading function is going to provide the logic to update that variable and then we'll have a source reader contract on the origin chain that uses x-call to send encoded call data and then have that routed by connects and called by connect system to ultimately update that greeting variable okay so if you have cloned this repository um you can go ahead and basically take a look at what's going on in this example so I I've I've done the cloning um I've done the sort of setup that's laid out in the preparation steps here uh which is including downloading Foundry which is another smart contract development toolkit um downloading make and also uh minting myself some Tesla tokens so the easiest way to do that is actually just to go to our test at Bridge UI and at the bottom here there's a faucet so you can select whatever chain you want and then make yourself a thousand test tokens on any of those chains but we're going to look at the greeter example here so that's in Source contract examples and greeter you'll see two contracts here that are basically fully implemented and ready for you to deploy so we're just going to talk about what's kind of happening in these contracts so starting from the source side this is the one that will be deployed On the Origin chain the first thing we do is we need to import these interfaces so the iconnects interface is the interface that will contain the x-call function and then irc20 is just going to help us um do some some token function calls you can ignore this interface for now this is just used for the scripts that we will go through later on the main contract here the only contract here is the source greeter and you can see that in the Constructor we're going to pass in the address of the connect contract on the origin domain and we're also going to pass in the address of the token that we're working with on this domain in this case it's going to be a test token because we're on testnet and the connects to address all of these addresses for the tokens and for connects to addresses can be found in the docs as well under resources so for this Workshop we're working with test that contracts and you'll find the the core contracts the assets um and all their addresses and all these available chains so back to the source breeder here in the Constructor we've passed in those values the main function that we're working with right here is X update greeting which is the function that's going to be called by a user to trigger this cross chain update so you'll see here that it's going to pass in a Target address which is the address of the destination greeter contract that we're going to deploy after this it's going to pass in the destination domain which is the specific domain of the chain so actually every every bridge basically has its own mapping of domain IDs so for connects we have ours listed here the first thing that's happening in this function is there's a required statement because the user is going to be sending funds to the source reader contract and then the source greeter contract is going to be sending that those funds into connects that first leg of the transfer is going to require that the user does a token allowance approval so this is just checking that that allowance has been done and then the user is going to have those tokens transferred into this contract this contract is going to do a similar approval to connect and we're going to have call data encoded which is uh which is going to be the call data sent into the X call so to understand like what you need to pass in here we need to take a look at what the destination greeter function requires so what we're going to be calling is ultimately this update reading function and you'll see that it takes a string memory new greeting so any string that you want passed in is going to be used as the new updated grading so for the call data that you need to pass into xcall is going to be an encoded string and you can see that that's also passed in as a parameter to this call so the user can specify whatever string they want the other parameters here are an amount so the amount of tokens that you want to send in with this and one thing that I forgot to mention at the beginning here for the context of this example is that um this greeter contract is actually going to require that some some payment is made to update the greeting so just to create a use case for y we're sending tokens into the call as well basically that update greeting function is going to be okay as long as you send any amount of test tokens greater than zero so that's basically what the user will be sending in here for amount anything greater than zero will work uh slippage is the other parameter that um needs a little bit more sort of context but for now we are just hard coding that to 10 000 and this is denominated in basis points so 10 000 is equivalent to 100 slippage and uh we'll talk about like what what the slippage actually means in a moment here the last parameter that we're going to feed in is the relayer fee so you'll notice that it's passed in as like as the value to this x call so what you're doing is you're actually passing in the native asset as the relayer fee and the real error fee is this fee that's paid to off-chain agents to actually execute your call on the destination chain so if you recall up here um once you do the X call and the the off chain relayers do their thing to to Route this message or across the chains somebody has to kind of trigger the call on destination like somebody with gas and in most cases uh you don't want that to be the user because some users might not have gas on the destination chain and um they will basically be constructing a meta transaction for relayers to submit on their behalf so in order to incentivize those relayers to trigger that that transaction of the destination chain you have to offer them some kind of incentive and that incentive is this relayer V that we're passing into the X call now um what you actually use as the real error fee is is something that you have to estimate and we have an SDK that you can use so back to the docs here under the SDK you'll find that the base class has an estimate really error fee function and you can basically construct this in any kind of like node environment or front end pull in the connect SDK and then call this estimate really our fee using the origin of destination domain parameters to determine how much this really or fee needs to be to incentivize relayers to actually to move the transaction along on the destination side we will eventually have off-chain method or unchain methods to do this but for now this is a fully off-chain calculation and for the purposes of this example we are actually just going to hard code the relayer fee as well to some value that is a conservative estimate that shouldn't that should go through all right so uh okay let's look at the destination reader now so um for the destination greeter you will also have to import a couple of interfaces uh the main one being this IX receiver um this this IX receiver says that uh any Target contract that's going to be called by connect has to implement an X receive function with exactly these parameters passed in uh we're not using a couple of these in the function that's why it's highlighted here but you can kind of just ignore that for now but basically if you are going to uh if you're going to capture any X call on the destination side via a contract that contract has to implement this function and you'll see that in the Constructor for this destination greeter we're also taking in the address of a token and this is going to be the test token on the destination side so in the xrc function we first check that the asset coming in from the X call is the right asset that we've instantiated this uh this contract to take in and it also is enforcing that cost that I mentioned earlier so anything greater than zero will be okay for this contract to um to update the greeting and then what we're doing is unpacking the call data that came in with the X calls so this encoded call data is being unpacked on the destination side so a new greeting is a decoded and we're calling update greeting with that decoded new greeting string and that's really it uh that's the source in the destination and um in the readme of the zap starter kit if you scroll down a little bit there will be a deployment section where we already have built out a bunch of different Forge scripts that can do the contract deployments and it can do the function calls without you having to you know muck with metamask and go to etherscan and um interact that way so there's a very simple make Command that you just run to deploy um The Source greeter and the destination greeter and you'll see that in the dot in dot example here there are a bunch of different variables that you need to fill out so your private key etherscan API key for verifying the contracts and anything that's between these like brackets are variables that need to fill out eventually we do have some um filled out already and this is assuming that you're sending from Gourley to optimism Gourley it already has the origin destination connects contracts the domains and the tokens filled out um it's the default is to send one test token to use a hundred as a slippage and I believe this is .03 eth as the relator fee so when you look at this and you deploy um your contracts it's going to it's going to show you the address of where that contract was deployed to so for the greeter example you'll want to fill out uh the address of that greeter and the address of the destination greeter and then you'll also want to update like what the what the new greeting you want will be because the next step after deployment is to actually test it so we also have um Forge scripts that call the X update greeting function here already and you can find all these scripts under your script so script greeter destinationreader.sdecil and sourcegreater.
s Dot Sol are the deployment scripts and then update greeting is going to be the script that actually calls X update greeting uh so what it's doing here is it is minting you uh some test tokens so um if you you know forgot or didn't actually make yourself some tokens earlier this script will actually do it for you automatically because the test token on testnet is freely inventable and then it will approve um the token allowance from your wallet to the source reader contract and then it's going to just call X update greeting with all of those parameters filled out most of those coming from the dot end so once you run where is it once you run the make update greeting command which is going to run this Forge script uh you'll see a transaction hash that pops out and you'll be able to actually track the status of that transaction hash so in the interest of time I'm not going to go through and actually deploy these contracts I'm just going to use um two pre-deployed ones so the source reader on Gorly and a destination greeter on optimism Gourley and we'll just take these addresses and we'll fill out our dot m here so down to the greeter um yeah I already have these filled out but the source reader address from orally and then the destination greeter address from optimism before Lee and then some new greeting that I want and then I will just run the update greeting command which is make update greeting this will start the forge script it will go through all those steps that we just looked at which is to Mint and approve and call update greeting forward is relatively fast so we should see our transaction pop up here all right there it is there's the hash on gorley if we look this up we can see that it is still pending but now the next step is to kind of determine at what state your transaction is in so you want to track like I sent the X call um what's its current status and so in our docs here we do have a way we do have a section to show you how to track these X calls one method is to query the subgraph directly so you can navigate to the reference go to subgraphs look at resources and then all of our deployed subgraphs are going to be here so you can do something like open the equal release subgraph paste in the sample query to find all of the uh all the details of that X call alternatively you can just look at our Explorer UI which is sorry it's in this page which is going to be here is that test that Dot connectscan.io and you can just input any transaction hash so the one that we just sent we'll look that up in the Explorer here and you can see that there's already a registration here so we're already tracking this this x call it has been sent on the destination on the source and we're just waiting to have that be received awesome there it is so after about a minute this was received and we can look at the transaction hash if we want to um just to see the assets actually being delivered so the test token was delivered and uh there will be another part of this that gets filled out called reconcile and that's a different state that's basically when the routers that are in our system uh providing fast liquidity get reimbursed but from an end user perspective the receive is all they all they really care about because that's when their assets get delivered so not only do the assets get delivered at this point in time but the destination contract also gets called so what's happening is that uh connects let me get back to the right page here on the receive Connex is going to call X receive on the Target contract so if we look at our destination contract again we're just here and uh you can see that we had this x receive um this x receive was called and the greeting was actually updated to what we what we specified earlier so this is an example of an unauthenticated call with asset transfers and this was this was pretty simple um the transaction went through pretty quickly and that's that's because we didn't actually have to wait um for any kind of validation by the bridge because the the function of the destination side was open and callable by anybody in contrast if there are a lot of use cases that require this sort of authenticated call and what we mean by the authenticated call is uh the destination contract so that X receive um actually requires some kind of permissioning so there's access control that's implemented in that function call so something like an only owner modifier where it checks like who's calling this function and do they have permission to call this function um that's what makes something authenticated Now with an authenticated call we actually can't fully short circuit this um this bridge validation and we actually have to wait out the full uh fraud proof window so that's kind of the drawback for authenticated calls you are able to um check who the original sender is and make sure that there are these access controls in place but you will have to wait out a much longer latency period for that for that call to go through so just to show you what this like authenticated call looks like we'll look at the greeter authenticated contracts so destination greeter authenticated and Source greeter authenticated are the authenticated versions of the greeter contracts looking at the source here it is basically doing the same things um uh I should mention that for the South educated contract just to make it or example just to make it slightly simpler we did not include an asset transfer as well so this one's not sending any tokens it's just doing a call data call but that function call on destination is going to be permissioned so for the source here actually everything is basically the same except for there's no token so no approval dance none of that we're we passing zero as the amount zero is a slippage uh we use address 0 for the token but we are still encoding the call data for this greeting so everything else is the same for the source for Destination however we're still importing IX receiver it still needs to have the xrc function but here we implement this only source modifier and what it's doing is ensuring that only the source contract is allowed to call the X receive on this destination so in X receive we do get the origin sender so the address of the contract that actually called xcall originally and we also get the origin domain so what we're going to go what we're doing here is we're passing in those values to the only source modifier and this modifier is checking that the origin domain and the sender address are from who we expect and who we expect is you know when we initialize this contract we deploy it we actually specify the origin domain and the contract so this source parameter for example would be the address of the source greeter authenticated contract that you deploy in Gourley and it also takes in the address of connect on this domain so the optimism Gorly connects contract would be specified here and then the origin domain would be go release domain so this modifier is expecting the original color to be that Source contract and for that calling domain to be the domain that you specified in the deployment here and it's also checking that the caller of this contract is the connect Bridge so all three of these requirements make it so that nobody else can call this function except for the source contract itself so under receive we apply that modifier to the X receive and we just do the same thing we just decode the greeter variable we send it into the update greeting function and it updates greeting um that's that's really the only difference in terms of in terms of implementation but it does have um a lot of caveats in terms of like the timing for an x-call to finally go through um in in most cases this is going to look somewhere between an hour to three hours depending on the source and the destination chains uh and and their canonical Bridge um latency windows but the sort of benefit that you get from this is that you can now create cross chain protocols uh cross chain calls that actually do require some kind of permissioning so this would be useful for Dow governance for example or anything where you don't want um like public uh public ability to call those functions and similarly to how we used Forge to deploy the greeter unauthenticated examples um the zap starter kit also has the same kind of commands to deploy those uh for the authenticated examples so um these make commands well basically look at your dot in file and it will take all the all the parameters here you will also want to fill out these addresses as soon as you're deploying them as soon as you've finished deploying them update the new greeting and then you can run um the X update greeting but for the authenticated version which is right here so update make update greeting off we'll call a different set of forged scripts for the authenticated example so earlier we were looking at these but um the authenticated examples will be using these forged scripts instead and these are actually simpler because there's no like I said there's no token um there's no token dance happening with the authenticated example it's just a super simplified example to show you how authentication will work and the main meat of that is going to be on the destination side where there's some kind of modifier or or within the cut the function that you're calling there's going to be a check for the origin domain the origin sender and making sure that the caller of X receive is connects all right so that's basically it um there is a slightly more complicated example that you can go through if you um want to mess with multiple hops with X calls and that's going to be this ping pong example so um it's what we call nested X calls where you can send an x-call within a next call uh we we have two contracts here ping and pong ping we'll call X call uh it will deliver some encoded data that pong which will also use that uh encoded data to call an X call back to Ping so in this case um ping and pong both Implement IX receiver so uh they both have this extra C function and they will both use xcol so this is an example of how you can kind of chain X calls together and they don't always have to go back to the same the same chains so you can do things like X call from chain a to chain B and the next call from chain B to chain C or you can go from chain a to chain B in the back to chain a and kind of use this pattern as a sort of callback mechanism all right so that is pretty much it in terms of looking at the examples but I did want to also run through some important considerations um which you will definitely run into when you're building uh these kinds of cross-training applications the first topic is we already touched on this a little bit but it's how do you estimate those relayer fees um there are actually two kinds of fees in the connected ecosystem one is the relayer fee which is the uh kind of like a service cost for relaying meta transactions on the destination and then another fee is a router fee so this is a hard-coded 0.05 fee of the asset that you're bridging which is going to be taken by routers for providing you the service of that sort of fast path execution normally all X calls have to go through well actually all X calls do in fact go through this sort of like slow path where it has to wait out the full uh window of the uh of the bridges validation window but if you're sending assets that our decentralized network of routers are providing liquidity for those routers can actually front the funds for the user on the destination chain and uh and basically provide them those funds ahead of time so the user gets their assets immediately on destination and the router takes up that lockup window for an hour to three hours and then they finally get reimbursed the funds so for their troubles of uh the lockup and fronting the costs um the user pays 0.05 fee uh to the router for that now for the relayer fee uh because you know we mentioned that there is this off chain method to calculate the relayer fees and this really depends on like gas prices and the current state of gas on the origin and the destination chain sometimes the estimate will be under what is actually necessary for the relayers like maybe there's a gas war and causes a gas Spike and the relayer fee you sent in is not enough for relayers if that's the case um the user has to bump their layer fee so there is a way to actually do this um directly through the contracts so you can call this bunk transfer method on the connect contract On the Origin domain or if you go into the UI for The Connect scan some transfers um you'll see here like this one for example has a low relayer fee and the user will be able to see like okay I sent uh the X call with 0.01 if that wasn't enough I actually needed to send this much more so um this scanner site actually lets the user bump their relator fee themselves and be able to see like what what the issue was with the XL if it didn't go through the other caveat that I wanted to talk about is um is that the relayer fees but it is the slippage so if you recall we were passing in a hard-coded slippage value for the greeter example the oh nothing unauthenticated example so Source greeter we were passing in slavicious 10 000.
so this means that for this example we're just willing to take 100 slippage on on the transfer um and that's fine for this kind of like play example but on mainnet on a real protocol slippage is something that your users will probably care about a lot and you should be allowing them the option to set the slippage as they want but similar to the relayer fees uh if your if a slippage is too low then um the x-call will not go through fully and similarly you'll be able to see the status of that and let's see if we can actually find one here so a low slippage error is going to look like this and for this you're gonna the user is going to be able to bump that slippage percentage and this is going to be done on the destination side so there's actually a little bit more uh complexity to this slippage component um and we'll take a moment here to actually just like dive into that so what what exactly is is this slippage uh why is there slippage when you're sending assets um yeah let's talk about that so the reason there is slippage is because when we send let's look at the assets actually when we send an asset from chain to chain the asset that we're sending and receiving may not be the assets that we actually want to use on those chains so every asset has a canonical domain meaning this is the domain where the asset was originally deployed to and then when those assets are bridged to other chains they are delivered as representations of the canonical asset so uh let's take a look at let's take a look at the test that ones for example we have two assets on test set test and wheat they are both canonical and gorley so this this specific asset is the one that uh the one that was originally deployed on Gourley and it's also the one that's used by everybody else so all the dexes all the other protocols they're all using these assets on Gourley so when I Bridge a test token from Gourley to optimism Gourley this canonical token is actually locked on the Connex contract on Gourley and then connects mints a representation token um of this of this test token on Optimus and poorly so uh when test is then bridged the other direction this test token is burned an optimism Gorly and then the canonical locked token on Gorly will be unlocked uh the the big distinction that you need to make here is that sometimes the representation token that's delivered on destination is not actually the token that everybody else is using so in this case the test token which connects uh deployed um is it it is the one that's being used on autism Gourley but with for example actually has a sort of a market decided uh flavor of that token that's being used by other protocols and testnet but the flavor of of wheath that connects to actually mints on optimism gorley is a different token so that's what we mean by a local token versus the adopted one the adopted one is the the version of the token that everybody else is using and the local version of the token is the one that connects to actually mints so because there is these two different flavors of wheath on optimism Gourley um we we want to give the users the one that they actually want which is which is the adopted version but connects mints uh the next version of that token instead so what happens under the hood during the x-call um on the Origins on the origin and the destination side there potentially may be a stable swap pool of the the next asset and the adopted asset and um because they're they're always uh because the next version of the asset is always redeemable one to one with the canonical um it has equal value and so we spin up these stable swap pools which you can see on our actual uh like this is mainnet and these are the stable saw pools for usdc and weath on these different chains so uh liquidity providers are going to provide um next next asset and the asset into these pools and then the X call is going to swap in and out of these assets depending on uh whether there is an adopted um or a difference between adopted and a local token on those chains so potentially for every X call you can have up to two stable swap pools that you're swapping in and out of and so the slippage parameter that we're sending in to the X call is your is your users uh acceptable slippage limit for both of those swaps every time you do a swap in a pool you incur some kind of slippage sometimes it's positive sometimes it's negative but at a certain point the user doesn't want to go below uh and and swap like one if for uh 0.99 for example and so they're they're limit at that point would be one percent and this slippage is the total submission amount so um if I am swapping a token from uh Gorly sorry let me get a good example of this if I am swapping there we go if I am swapping a adopted token from a chain where it's it's not canonical so um usdc on optimism is not canonical and usdc on polygon is also not canonical if I happen to swap the adopted version which is usdc which is most of the time connects can only connecting the only Bridge the next flavor of that token so when we're doing a next call and sending usdc from optimism to polycon it's first going to be swapped from usdc to next usdc which incurs some kind of slippage and then at the destination side um connects is going to Mint next to usdc and then swap it into usdc to be delivered to the user at the final side so in this case there are actually two swaps that are happening you you are free to to send the next version of these as well from chain to chain or receive them if you want and in those cases you will not increase lippage but for the majority of cases and for most uh composability purposes with other protocols you are going to be sending it receiving the adopted asset and so this slippage parameter is actually uh very important um and sometimes you will have set the slippage too low and in those cases you'll have to go and actually bump that all right so I know that was kind of a lot to digest um the connects team is always available on our Discord server so if you have questions about any any of these examples any part of the parameters the slippage the relayer fees how you build other kinds of contracts we're always there and active to help out so you know don't be a stranger feel free to drop by say hi and ask us questions um yeah that's basically it um I know these were some very simple examples but uh getting started here well we'll kind of get you at a good state to start building your own protocols uh and exploring this like wonderful new world of cross chain applications
Automatic transcript — names and jargon may be misspelled.