New Ethereum talks, every Monday. The week's conference uploads by event, in your inbox.

Loading player…

Introduction to Declarative programming with Essential - ETHGlobal San Francisco 2024

ETHGlobalThu, Oct 9, 2025, 12:00 AM

Join us for a workshop with Mohammad Fawaz from Essential titled, "Introduction to Declarative programming" This workshops anticipates ETHGlobal San Francisco, a 3-day hackathon happening from October 18 to 20, 2024, bringing together the most skilled web3 developers, designers and product builders from all around the globe for a weekend-long adventure to advance the Ethereum ecosystem! ----------------- Follow us: https://x.com/ethglobal Check us out: https://ethglobal.com/ Email us at: hello@ethglobal.com

Transcript

hello everyone uh my name is Muhammad I am part of the central team uh we are building the first declarative blockchain uh and this is a workshop on how to build declarative applications using our smart contact language bint and deploy it on the central chain and test it out now um as a prerequisite for this uh Workshop you should look at the getting started guide that you can find on our docs website docs. essential. Builders um the getting started guide will walk you through the installation process how to get into or enter into install and enter a NYX shell and the Nyx shell will have all the tools you need uh to start building and uh the guide will also walk you through how to build a simpler project uh particularly a counter example uh which basically has a counter in storage that increments by one every time you submit a solution to it so I highly suggest you walk through that um and make sure um you you complete that guide before watching this video um I think um walking through a a more complex example in this video is a little bit more uh useful for you so um so by now you should have and I have it here um this is a a NYX shell which I've entered um and you know as as explained in the getting starting guide you have to install next and enter a next a next developer shell um all that the the commands are in the guide um once you have that you'll find that you have tools like point so if you do which point it will link you to the bind tool which allows you to to build um uh bind contracts so uh what we're look going to look at today is a token example um token is a little bit more complicated than the counter it has slightly more interesting um things going on and then particularly uh authentication and and uh some other things so what I'll start with is um I need to First create a directory called token and then I'm going to use the pound find tool to create a new project called token under token slash uh sorry under token slash contract so I'll I'll make the the contract under a directory under these directories if I go here I can now see that I have a tble file which describes of my project and I also have a source directory in which I have something in here I have a basic counter example which I'm just going to get rid of um and then I'm going to start working through the token example so uh let's think through what the token needs um the first thing that token requires is is some sort of a map between uh addresses or accounts and values so for that we need a storage block a storage blocks looks like a storage followed by a list of variables in here and for the tokens specifically we need among a bunch of things uh we need a balances map and the way we write a map in in in uh in pint is via the syntax which says I'm mapping uh b256 to integers so b b b256 represents addresses 25 256bit addresses to integers which are my balances uh I could put other stuff here like the total Supply and maybe a ticker for the token and all that kind of stuff um but I'm not going to worry about not going to worry about that just yet um now each point contract has uh a list of predicates so um a predicate is basically a collection of constraints um again we're talking about a declar declarative language um so so we're not actually describing how operations work or how state gets updated we're just describing what we want this state to be or what we want the future state to be and for that we just list um a sequence of constraints that constraint my state um let's start with a predicate called mint this is how we declare a predicate just a predicate keyword Follow by name followed by the name of the of the predicate and uh we're just going to start by adding a list of uh decision variables now a decision variable is is basically a local variable that every solution has to set think of it as a parameter for the predicate it is not a state variable it doesn't live in storage it's just a temporary thing that the solution sets and then it it basically parameterizes the the the predicate in some way so how do we want to parameterize the mint uh predicate we want uh a variable called two uh of type B6 VAR is the keyword here to declare a decision variable and then we need an amount so the idea with a mint predicate is to um create a certain amount of tokens called amount and send that amount to an address called two which is why the type of two is b256 now I I don't initialize these things these are things that are set in the solution which is why they're just variables now um let's think through some of the constraints um the the first constraint um I could put constraints like I need the amount to be greater or equal to zero this makes sense like if someone sets a negative amount here it kind of doesn't make a lot of sense I'm not going to Mint a negative amount of jokers so this kind of a safety constraint it's it's something that if it's this is this is a constraint on the parameters so if this is not set then or sorry if this constraint is not satisfied then uh the predicate will fail and and the solution will not proceed will not be accepted now what I need to do is to increase the balance of two by amount assuming all the the conditions are satisfied so how do I do that the first thing I need to do is to access the location in storage where the balance of this particular address is stored so how do I do that I need to create something called a state variable and this is a variable that that starts with the word keyword State and I'm going to call that uh balance um and I'm going to initialize that to storage column column balances of two so this is saying access this storage map at the with the key to right so I'm just going to because this is bad this is a map from addresses to inss I can pass in two here in between brackets and what I'm what I'm saying here basically accessing this particular storage slot there is no actual reading happening this all happens at at solve or validation time this is just a description of what I want balances to or this balance variable to be um so now that I have the balance I can say Okay I need the balance to increase by amount right this is what what the mint does the mint predicate does it basically creates uh some amount of tokens and then sends them to balance or to not send them to uh this particular address uh and the way I do that is via this syntax that says okay so I need my balance Prime balance Prime is the new balance and I'm going to set that that to balance plus amount so this is saying increase effectively in uh increase the amount of balance the current balance by amount so the new balance is going to be the old balance plus amount and this is notice that this is a bulling expression it just says that the future balance is the same as the current balance plus amount now what other constraints do I need um I need a constraint like this and I I'll say what Mentor is uh minor is effectively a specific address that I'm minting to um now it's probably the case that only some particular owner is allowed to Mint stuff to themselves SP tokens to themselves and so I'd like to restrict uh the uh the person or the the account receiving um the tokens to be a particular address called Mentor now this is a con uh con Mentor which I I can declare at Global scope here I can set the type for it if I want it's optional and I'm going to set the value to I'm going to copy it from here and I will explain what this where this number comes from that's going to be that's going to come in handy later on when we look at the tests so we've made sure that the the accounts receiving the tokens is actually Mentor which is good uh let's see what else are we missing um we're missing authentication so we're missing the fact that we want to make sure that the account that's actually making or the account that the solver is proposing a solution on their behalf is actually the minor right so this is our uh our version of authentication authentication in in essential you kind of have to do it manually and I'll show how in in in a second um so for that I need to start with a signature so a signature has to be provided by uh the solver on behalf of um the um on behalf of the user or the account that's requesting the mint um now a signature this is this is a type that I'm going to create here want to create a type use for and it's just that's what a signature looks like it's a topple that contains it's effectively uh nine words um um note that for in the essential VM every integer is a single word and every b256 is clearly um is four wordss because integers are 64 bits so every word in the essential BM is 64 bits now a signature is just a sequence of words like this and I am going to uh call a macro here which I will declare uh later uh validate signature and uh I'm going to validate a signature you need three things you need the U the data that you've signed on you need the address and you need the signature itself so I'm just going to do that here going to pass in the signature I'm going to pass in the two which is the account and I'm going to pass in the data that I'm going to be signing over so what this is saying is I'm I'm I'm passing in the signature the two and then a topple that contains the data that I'm going to be signing over which is the rest of the the decision variables now how do how does this um I'm going to copy this particular macro here because you're probably going to have to do the same and this macro is takes in three things and returns this expression so this expression is a Boolean naturally um it basically says sign over the data sorry uh hash the data and then recover the public address given the hash of the data and the signature and then you hope that the return public address has a hash that is the same as the uh the key that i' like to validate so in this case what we're saying is when we call the the macro with these things we're saying we're passing in the signature the two and the two comma amount so we're hashing the data then we're saying recover the public address given the hash of this data and the signature and then make sure that the resulting public address if you hash that because the public address has fields in it um you uh you high that you get back at b256 uh and the result of that must match the this this particular basic two here so now we validated the signature uh and I think we're ready if we um we want to basically compile this so we're going let's see where we are uh okay we're going to go to the contract uh project or the token project in the nick shell we have and then we're going to call we're going to write find build so find build and this is going to build my contact and it it passes fine it gives me um you know the output here contains the address of the contract as well as the uh address of the predicate these are the content hashes of the contract and the predicate respectively these are useful for deployment or for referring to specific predicates or contracts from other external contexts so now I I have what I need um the uh the Min predicate has been written and I have everything in now there's one little caveat here with what I wrote um you you may be wondering what happens if uh balance here has not been said before so what does that mean let's say this particular address has never received any tokens before which means it has no entry in this map now you probably have seen that in the in the contract or the in the counter contract and the way I I I prevent from anything bad happening is by first checking if balance has been set before or not and the way I do that is via by checking that balance is nil or not so what I do is say if balance equals nil otherwise do something else so if balance is not nil what I'm going to do is I'm just going to do this basically make incremented and if balance is nil I'm just going to set it to amount because I'm going to assume that the previous value was in fact zero so it just hasn't been set in the in the map before so this particular address or this particular account has no tokens so the new uh the new um balance is going to be just amount um let's let's build again let me see if I'm forgetting anything I don't believe I am um in fact there's there's one thing that I will uh yeah so mutability uh this is something that you probably also seen in the counter example I need to declare this particular storage access as mutable and the mutey keyword comes on this side which I know is a little bit weird but in the future we're going to be able to pass in arbitrary storage um or storage axises here which means you can set each particular one as mute so mutability applies to the storage axis which is effectively saying the key where this thing is stored is mutable and not the state valuable itself so this is saying M allow the key where this particular expression or this particular storage uh value is stored allow this particular key to be changed in this predicate if I don't have mute here then the VM will panic because it will see that I'm trying to uh modify a key in this in this matter um where the solution is going to try to propose a value for balance but in fact this particular key is not allowed to change because everything is non mutable by default um so I need to set I need to set this particular key to mutable uh this is a very very important point because if I don't have um if things are allowed to be changed by default then uh solvers could submit any solutions and modify any storage slots they they feel like it uh they could improve their own balances they could improve anyone's balance they could decrease anyone's balance and nothing there is nothing is there to prevent them so making sure that every key is immutable by default and then you have to opt into mutability within the predicate that you're trying to solve is is important it's kind of a security concern so this is the Min function it's pretty pretty nice um now I'm going to uh build kind of a simple application around this contract and hopefully test it out so uh I'm going to come here get into the token uh directory and I'm going to create a new rust project uh cargo new uh Library counter app or the token app I should say going to in here and the first thing I want to do I'm going to have to add a bunch of dependencies now will copy those um and you can do the same I will have I will link to this particular uh you'll have access to all of these things via GitHub uh link later um so this is the the toml file where all the dependencies exist and then um I should be able to go back here and go to the Token app and then at least build so if I build this it will take a little bit of time so while we download all the dependencies I'm going come back here and I'm going to just erase everything in the lib file and I'm going to add my own stuff so let's see what do I need here um again um I will copy a bunch of things so we haven't the the transfer function so I'm going to comment this out and so let me see all right so this works fine so what do we have here so I'm I'm building basically a periphery application or a front end if you will um that is able to interact with the contract so what does that mean so I'm going to start with uh a struct called app which is just my application that I'm going to be able to create instances of in the tests and this app takes in a client a wallet and a bunch of addresses these addresses are basically the addresses of the token um contract the contract itself then uh the the addresses of all the predicates and token um as I said before these are BAS basically the content hashes of the corresponding code um one thing I'll I'll need and I didn't I didn't talk about um the ABI yet but the ABI is is a file that gets generated by uh the Pint build Tool uh once you build a contact you will automatically get an AI file uh which I should probably show here uh token contract and to find that file you look in the out directory of the contract that you the contact project if you look here it's kind of self-explanatory it basically lists all your predicates and lists your storage so this is kind of adjacent representation of your storage block says oh I have balances here um which is of type map from Two and these these types and then uh I have a you my predicates I have a single predicate so far called mint has some vars each VAR has a name and a type and and so on so it's pretty self-explanatory so this these are all the things that a solver needs to know about to be able to set a solution or to be able to reason about Sol same with with with validators um and the way we would use this file um is via an ABI gen tool so it's it's a macro that you can access via the B ABI crate and it takes in uh ABI basically uh a path to your ABI file which is in this case lives in my contract directory contract gug and and the contct itself now if you look in here you'll find another file in the in the output SL debug directory uh which will not make a lot of sense to you but this is just the Json representation of the bite code that represents the contract so you don't need to read this but uh obviously tools are able to par it now what this does is it will allow you to access all the types that are created in your contract and allows you to basically read and set storage variables uh or storage State mutations as I will I will show later um now I need to start implementing functions on this application I'm going to start with a new function which is pretty straightforward it just sets the client new function that takes in a server address um that's that's all border plate code that I suggest you mostly copy and then I need a Min function now this Min function effectively because when you know the Min function typically in in imperative uh ecosystems you typically make calls to the contract to test theat um for us there are no or an essential there are no calls there are no contract calls all you do is propose Solutions and to propose a solution for the mint predicate what you do is you basically have to craft a solution down here which uh I will show I will explain in a bit you craft the solution and you just submit it and then you hope for the best and what that means is you submit it you submit the solution and you hope it gets picked up by a solver and when when that happens if the solution gets accepted by the validators then the State updates proposed in the solution will in fact go through or in fact be committed um so let's look at the Min function briefly um we need to craft a solution as I said earlier so a solution is basically an object that looks like this it has a bunch of solution data so it's a list of solution data objects and each solution data solves one predicate so in the in in our case we only need to solve a single predicate but in other cases you may want to solve one predicate from each contract or multiple predicates simultaneously from from many different contracts or within the same contract it doesn't really matter um and this is important for applications that need to interact with multiple contracts so if you're writing an exchange application for example then you you probably need to do that because then you have to interact with your exchange contract as well as the token contract so this is something you might want to look at or try during the high uh so what is a solution data it's basically starts with predicate to solve which says you know this is the predicate that I'm trying to solve it's basically the address of the predicate which is what I got here by the way if you go back to contract and you do build this is the token or this is the the mint address it's basically a combination of the token address so the contact address and the predicate address the decision variables I need to set the decision variables I go back to um um to my my token or my contract if I go back here I'm GNA I'm going to open it in this window um I have three decision variables two amount and signature right so because I using the AI because I've done I've made this micro call here I have access to an object called mint colum colon vs now uh mint is the name of the predicate uh from the ABI generated uh code and vs lives within this module and vs has all my values so it it vs has expects three fields the two the amount and the signature so uh the two is basically um the address which in fact matches this one here and the way I've done this is by calling this Kash key which um is defined later and basically accesses the the wallet so the wallet which I'm not going to go through too much here you can you can study that on your own but effectively uh it's saying you know get me the public key for this particular account name the wallet has a list of accounts uh a map between the account names as strings and their public keys and I'm going to take that public key and I'm going to get the hash of it um so that's what two is so I'm going to get back at an array so if you look at what the type of this is it's in fact I believe it's an array of four I 64s yes uh because that's what the b256 is it's four words and every word is I64 so that so this is the first decision variables I'm going to set two here I'm going to set the the two to this particular key which in this case is is the minor uh the amount is what I'm passing as a parameter for this function that's how much I want to men so when I'm testing this function I can set this whatever you want and the signature is just I just need to sign over the data so what do I seem to sign over as I said before sign data is basically another function that I've defined later and it basically uses U some aign words method from the wallet um and uh we're just going to encode it in a very specific way and the way we do this is because the the signature remember if you remember here the signature uh type is basically b56 b56 and an integer so I need to encode the signature as such so it's basically that's an array of four integers so b256 and then that have that be another b256 and then an integer so that's how I encoded the uh the signature now I have the signature I can just pass it as a decision variable as well now that I've set all the decision variables I need to set the transit data which I'm not going to talk about too much here you can look at some of the more uh uh complex examples and integration repo or the sample app repo um and then finally the state mutations uh this is where the magic happen State mutations are basically say we say storage colon callon mutations all of these are available as part of the um the ABI gen that I've done earlier and I'm going to take the balances here the balances map notice that this is available here the same name that I used in the contract and it says I'd like to map to to uh this new value so I'm mapping the Bal I'm I'm setting effectively the balance of two the key that will receive the minted tokens to current balance plus amount right which is which is kind of clear um now that's what the solution is right it's meant to give me the new balance or propose a particular State update that when I check it later in the contract it it's actually correct now current balance I need to get that so how do I get current balance I need to query the current state and the way I do that is I show later in this it's it's a helper function that gets me the balance of a particular account um so yeah you get I just happen to give it account names as strings and so we get the the key out of that first and then uh we created the state so to create a state we need to get the location where uh this particular value is stored or where the balance of this particular account is stored and the way I do this is using the keys interface similar to mutations above Keys is very similar here it says Get me the keys um give me a list of keys and the first of them has to be uh the place for the balance of account key lives so I'm going to get a vector that contains in this case just a single key so I'm going to use it here and that's the query State function or method on the the client allows me to get get back uh a value and then I I have a match statement here that says if you don't get back anything just set it to zero which is kind of reasonable this says if nothing has been if this particular key has not been said before then I just what I get back is zero so no balance at all um so yeah that's pretty much it now that I have the current balance I'm going add amount to it I'm going to propose that the new state value is in fact current balance plus plus amount and then I have my solution which is as I said is it's a vector of solution data and in this case it's just a single solution data that I have to deal with uh and finally I'm just going to submit the solution pretty straightforward um that's pretty much it um let's see how we test this so I'm going to go to back to the Token app and I'm going to create a test directory and I'm going to add a token. RS test I'm going to paste some more stuff here I'm testing the mint right so again there all of this is just border plate code uh won't go too much into it uh so I'm going to remove everything to do with trans for because we really haven't looked at that see if that's going to work all right so um test mint um we're going to have a bunch of Border plate code here we we're basically setting up uh wallets and setting accounts for each um party in this case we're only going to need Alice which is the person who is or the account um that is allowed to Mint um and so the first thing we'll do um the first thing we'll do is set all the addresses um which I'm basically setting those using the content address um method which as I said the the addresses of predicates and contracts are the content hashes of uh the corresponding bite code so I just I'm just doing that manually here u i could I could pick them up from the command line if I want um kind of the same idea uh and then I'm going to deploy the contract right so sign and deploy uh we take the contract uh token contract object and I'm just going to deploy it and now I'm ready to do whatever I want so I'm going to create uh an instance of the application with a with the server address and then I'm going to start doing things so the first thing I'm going to do is query the balance of Alice which so since nothing has happened so far the balance should be zero remember that I you know when you quate the balance in fact I'm calling this function here so this this token. balance is in fact this function which queres the balance from this from the from the blockchain and if the balance does not exist I'm just going to return a zero so which is it's exactly what happens here then I'm going to submit a solution uh I'm going to submit a solution to for the mint function uh and the way I do this is um by calling the mint U method here which remember does not actually make any contact calls because that does make sense for us in a declarative con u in a declarative context I'm just proposing so the main function is I actually just propose a Sol a solution and the solution it's going to propose is to to Mint this many tokens 100,000 tokens to Alice right so I'm just going to go back here when I call this an amount will come in here it's going to read the previous balance it's going to add amount to it and it's going to propose the new value or the new balance of um for Alice is in fact uh 0 plus anount so I can now once I do this I'm going to wait a little bit and then I'm going to check the New Balance I'm going to check the the balance here and I'm going to make sure that in fact the new balance is the mint amount right it's just because I had because Alice had had nothing before um and I'm going to repeat the same thing um I'm gonna this time I'm going to Min 200,000 tokens I'm going to do the same call Mint I'm going to wait a little bit I'm going to check the current balance I'm going to make sure that the current balance is actually the first amount plus the second amount so the current balance is now uh 100,000 plus 200,000 which should make sense that's it that's it for my test now what's really happening here just some some some conceptual things to to uh keep in mind when we submit when we call submit solution and then we wait and then this particular um solution gets validated um because I have predicate to solve set to a particular predicate which does match this particular predicate here um what happens is this solution gets propagated and BAS basically the validator all the the essential VM will plug everything in the solution and make sure that every constraint here is satisfied so we're going to make sure that the amount that I set here satisfies this constraint otherwise the VM will return well the predicate will fail the VM will panic and the solution will not be accepted and I'm going to uh check that the new balance is the old balance plus amount I'm going to check that the account that I'm minting to you is minor which matches the address for this for for for the account for account owned by Alice and I'm going to validate the signature so the signature that I set here using signed data must match uh or must satisfy this constraint which is really important so all of that's happening every when when once I submit a solution and the solution gets validated so the valid process which happens happens during uh the sleep time that I have here when I submit a solution and I wait during that period um the solution is going to get validated and when it's it gets validated and all the conat are satisfied then the state is going to be updated um so yeah that's that's pretty much it this is uh how I would write a mint predicate and how I would uh write a periphery or a front and forward and test it out uh you could you could try the same you could try to implement a transform function and do the same shouldn't should be kind of a natural extension to this um and what I recommend is you look at the uh repository which it's kind of uh where is it so this repository over here uh will contain everything I talked about uh including the counter example um and most importantly um you should check out uh the the sample apps repo which I'm linking to here if you go to sample applications this has full examples nft counter token uh that are in fact doing more things so there's a few things that are missing in the token example um there's the nons that you have to deal with and and uh there are more there's more complex code that handles authentication for accounts that are not EAS uh so validating signatures here is is somewhat easy because we're dealing with accounts wallets and we're not dealing with other contracts but to make the token contract more interesting you need to be able to handle signatures or handle authentication for accounts that are other contracts or other other predicates and uh this you know the thiso this token example shows you how to do this it's a bit more involved and I suggest that you study it well um so yeah and really everything you know this this uh this repo has more links there's the quick start guide which uh I talked about earlier the reference for the bint language and the specs for the for the protocol itself if you you need that and uh yeah that's pretty much it just keep in mind that all of this stuff is still work in progress um there are certain limitations to the language so far so for example we can we don't have Dynamic arrays we don't have Dynamic vectors in storage we don't have uh a way to Loop dynamically over things um those are not really required for many applications I believe you should be able to um experiment with many different things without those things um but just something to keep in mind um and yeah and I think I will I will end it here um hopefully this is kind of somewhat useful and will help you get going with with building uh whichever application you have in mind uh lots of things you can you could look at uh even even just looking at a different implementation of the token contract and looking at how you can improve authentication how you can think about you making it more modular or making it simpler um looking at name Services looking at um escro contracts or other you know defi in general derivative applications stuff that you know anything you can think of uh experiment with it um so yeah it'll be interesting to see what you guys come up with and uh good luck

Automatic transcript — names and jargon may be misspelled.