Integrating Self Hands on Smart Contract SDK Workshop I Ayman Mohammed
ETHGlobal·Thu, Oct 9, 2025, 12:00 AM
Ayman Mohammed leads a hands-on workshop on Self, a privacy-preserving identity protocol for Sybil resistance and selective disclosure. Learn how Self keeps PII out of centralized databases by storing data only on the user’s device, then proves facts with ZK proofs (age ≥ 18, not in forbidden countries, OFAC checks) without revealing raw data. The session covers the front-end QR/deeplink flow, TEEs for proof generation, on-chain SDK (Solidity) and off-chain SDK (TypeScript/Go), setting verification configs (scope, minimum age, excluded countries), deriving a scope seed, and wiring contracts (constructor with Hub, custom verification hook, setScope). Ayman also demos Aadhaar support plans, passport MRZ/NFC flows, mock documents, failure paths, and bounty tiers for on-chain and off-chain integrations. 00:11 Introduction 00:50 Why Self: no centralized PII storage 01:28 Sybil resistance via passports/IDs 02:19 Selective disclosure: attributes & ranges 03:14 Proving “over 18” & country exclusions 03:42 OFAC privacy-safe compliance checks 04:32 Adoption data & upcoming Aadhaar support 04:57 How Self works: MRZ/NFC & commitments 05:55 Aadhaar QR flow & commitments 06:21 Disclosure proofs & on-chain tree 06:47 One-person/one-nullifier model 07:28 Integrator flow: QR/deeplink to app 08:03 TEE attestation & ZK proof pipeline 08:40 SDK options: on-chain vs off-chain 09:11 Workshop repo layout (app & contracts) 10:29 Front-end QR SDK & disclosure config 12:05 Minimum age & excluded countries example 14:09 Contracts overview (ProofOfHuman) 15:00 VerificationConfig & config ID 16:41 Custom verification hook usage 18:12 Deploy script & constraints match 20:04 Scope seed derivation & setScope 21:09 Live QR scan & successful proof 24:02 US doc mock: proof blocked demo 25:21 Tools.self.xyz for scope generation 26:54 Off-chain verifier quick start 29:15 Bounties, docs, and closing _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 🇮🇳 *ETHGlobal New Delhi* This workshop is specifically for ETHGlobal New Delhi, a 3-day hackathon held September 19-21 at the India International Convention & Expo Centre, 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! Watch the full ETHGlobal New Delhi 2025 playlist here: https://www.youtube.com/playlist?list=PLXzKMXK2aHh7zCFW3hBqatZ18rQaYeEJL _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ✅ *Follow Self* X: https://x.com/SelfProtocol ✅ *Follow ETHGlobal* X: https://x.com/ETHGlobal Warpcast: https://warpcast.com/ethglobal Website: https://ethglobal.com YouTube: https://www.youtube.com/c/ETHGlobal _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _Are you interested in Ethereum development and entrepreneurship?_ 👉 Sign up for the next ETHGlobal event: https://ethglobal.com/events 🎁 Get exclusive access and perks with ETHGlobal Plus! https://ethglobal.com/plus 📣 Want us to throw an event in your city? Tell us where! https://ethglobal.com/city
Transcript
[Music] Hey guys, welcome to the H global New Delhi workshop where we'll be going through what self is and what you can do with self, how you can and how you can integrate it in your project, store everything inside the databases and you don't know where that data goes eventually and Um well obviously people know who you are when when your data is stored in an unsecure manner and there's so many leaks that target these databases and um yeah KYC um providers and well if so the the the good thing about self is if nothing is stored that automatically increases the security. So everything is stored inside your app and there's less points in which um a leakage could occur of your data and obviously this is also great for user experience since users don't really have to worry about their data getting leaked eventually and yeah so what exactly can you what exactly does self offer so one thing is civil resistance basically so the idea is passports are difficult to obtain So since they are difficult to obtain obviously they provide a natural mechanism for civil resistance because um one thing is they are difficult to obtain there's one only um one issue per person um among a country and they are obviously easy to work with because they have a machine readable zone those two lines at the bottom of a passport and also an NFC chip for um where all um the signed data resides and the signatures. and everything. Obviously, if your um um not all countries have the NFC chip inside the passport, we we we are also actively working on ID ID cards that don't have the um NFC chip embedded inside them. So, this is the civil resistance property offered because passports are naturally or any national ID is hard to obtain and duplicate and be forced by a person.
So what can you do? So one property is civil resistance like one nullifier per user and the other property is selective disclosure of your attributes. So for example you can reveal your name, date of birth, issuing state, your nationality and gender. So these are the attri that are present inside your passport. So you can disclose them as it is.
But in self we also offer some additional um properties that you can disclose about yourself without um actually um revealing stuff about you. For example, instead of just revealing your age, which you can reveal that you're above a certain age, it gives you a range of um well onlookers can have a range of um ages to work with, but not the actual age. The same applies to a set of countries, right? um you can prove that you're not from a set of countries without revealing your actual country as well and the fact that you're not in any of list. So the of list is basically a US sanctioned list.
So what it does is um the offac list has um a big it's basically a big list of all the um offenders criminal offenders and so on of people. So for people who are looking at compliance, you can you can take a look at the offac feature provided by self to prove that a user that's registering with um in your app is not part of the offax list. Also this happens in a privacy preserving way. So if someone is part of the offax list, they will never be um part of your protocol. So how how exactly can you use the app and how can you um register or play around with self protocol?
So before we um take a look at how self actually works, we can um take we can see here that um here's the data provided by A16Z on the amount of people that are um using passports and biometric ID cards and so on. But uh you can see that um obviously not everyone has passports or biometric IDs. So we are soon rolling out support for Aadhaar which would unlock 1.4 million people to on board with self and along with your protocols as well. Moving on to how self actually works.
What happens is for passports at least what you would do is scan the MRZ which is the machine readable zone. It's like the bottom two lines of your passport and um that essentially acts as a key to the NFC chip inside your passport. So you scan you scan it and then you use that to access the chip and authenticate yourself with it. So what self does internally is it registers users inside the protocol in an onchain tree where um where it's used later on to prove facts about yourself. So obviously for Aadhaar there's no chip or there's no um MRZ.
So what happens but if you notice there's like a QR code inside the Aadhaar um card. So this this is present inside the M Aadhar PDFs or the OEI PDFs as well. So what you would do instead is take a screenshot of those QR codes and upload it to self where we can decode your data and um store a commitment of your data. You can prove facts about yourself eventually in the future. The part of proving facts about yourself is called a disclosure proof.
Essentially what you do is you reveal parts of your um committed data. And what you're doing is you're proving the fact that you exist inside this tree and as well as revealing data about yourself. So if um how this so going back to a point about stable resistance, what happens is since people can't register twice, they can only um disclose um yeah. So so people can't really register inside the um tree twice. So what h what also happens what's like a natural um follow to this is that you can also disclose from a single commitment and not like multiple commitments.
So you can't really have double proofs and so on. So how how would you integrate um self inside your app? So what happens is as a third party integrator what you would do is if you're building a website you would have a QR code. So what this QR code initially represents is um data about what you about um your app itself. And yeah, this is or or if you're developing an app, it's going to be a deep link that has it's like a button that can um eventually take that um when scanned or clicked takes you to the self mobile app.
And what the self mobile app does is um it encrypts your data and passes it to a T where a zero knowledge proof is generated. So the extent of your data is only stored until here inside the T and then it's destroyed. So you can the app obviously verifies the attistation of the T and if it um is not what it accept expects then it's obviously not going to pass on your data to the T from the T is it goes to your contract or your endpoint. So you can either um integrate self using onchain using the onchain SDK. So in this case your contract will be called with G or you can um integrate us into a backend inside inside like an offchain SDK which is TypeScript and also we also have Go SDK that you can integrate in your apply what you would do is you would um integrate this part the QR code or deep link which is the front-end SDK and one of these um SDKs either the onchain or offchain one.
Moving on to the bounties. Uh we we we're offering um three tiered bounty for the onchain integrations. So the ones that use this um backend SDK um sorry um the contract SDK. So this is like three tier. So first price for first price is 5,000 and 2500 and 1500.
And so for offchain we would be giving um there's a 1,000. We will be doing a dry run of this. So what what exactly um so you can so you can take a look at selfxyz/workshop. We I can I'll be cloning that and running um the app in a bit. But there's the app directory that has the front end and it h it integrates the QR code SDK.
And what it does is it has data like I said it has data about your app like your endpoint where your proof should be sent to and so on. And the mobile app scans this QR code. And what you can do is once it scans verifies the addestation of the T, it pull it um sends the ZK proof to your um back end in this case the smart contract. Yeah. Let's take a look at the workshop.
So there's um if you go to self XYZ/workshop. So you can take a look here. is um we'll be cloning this here and yeah let's see the cell and cloning the workshop by the time it takes to get cloned we can um chat about the front end SDK so the front end SDK is going to be self xyz/qr code it's essentially a wrapper it's like it provides data about your um protocol so that self knows where we can pass it along um where we can pass the proofs along how we can verify it whether it's on mainet or test net. So if you go here, you can see there's an app directory and there's a contracts directory. So cd into app and install the dependencies.
You can see there's the self xyz QR code library and you can see it's on version 1.0.13 0.13 and there's um other libraries but the main one for the front end integration is 1.0.
13. So how exactly is the QR code library used? It's a simple um component where you import a self app builder has data about your app, what version you're using, the name of your app, the scope where your data should be passed on, the logo of your um protocol, your app and you can also pass along user ids which is a way you can identify your use um users. So you can either pass a zero ID or an Ethereum address. In this case, since it's a simple example, we're passing the zero address.
And since we are using Ethereum addresses, it would be hex. But if you would use UID, you would change it to EU ID. And yeah, you can also pass some userdefined data. And your protocol can decide how to act based on this userdefined data. So you can see you can um choose different types of um configs to verify people from um based on userdefined data.
So and this is the main part actually. So it it has the disclosures. So what what exactly are you disclosing? But what so what this QR code tells you is you should we want your app wants to um disclose that the guy has at least a minimum age of 18 and he's not inside the US. So you're not disclosing that the guy is in the US.
You're disclosing um you're just um verifying that he's not inside um a set of countries. In this case is only a single country which is the US. And you're also disclosing the nationality which um is not really necessary or the gender. So you can see there's a bunch of um attributes that you can also disclose apart from the custom ones that we added on top of such as the OFAC or excluded countries and the minimum age. And let's also add install the dependent.
Let me show you what the friend SDK is. You build a self app builder that has data about what you want to disclose and so on. And this is inside a QR code wrapper which takes in the self app and optional on success and on error methods. So that's the front SDK which is pretty simple. So keep in mind that we'll be only disclosing whether the whether the guy is um minimum age of 18 and his excluded countries is um he's not in the US.
Basically this is the front end and um you can check the contracts. So let's see the contracts are done. Let's also install the foundry library. Yeah. So that that is the front end.
This is so we'll be taking a look at the contracts as well. So if you go into src you can see there's a proof of human contract into the constructor you'll see it accepts the hub address. So you can find the hub addresses for the protocol in docs.self.xyz.
So you can find it um in deployed contracts. So on mainet it's this one and on testnet it's this. So we we we are adding support for sepoleia right now. You can see there's a constructor that takes the hub address and the scope. So notice this scope is um essentially an identifier for your applicers doing traffic analysis from identifying people people who um do multiple disclosures in different apps.
So different scopes kind of hide your identity within a protocol and it makes you less vulnerable actually um to traffic analysis attacks. So you can see um you we accept a verification config. So what verification config is? It's a way of enforcing your config on chain. So you saw our app wants to disclose um the fact that the person is the person's minimum age is 18 and he's not inside the United States.
You want to enforce the same constraints inside the contract as well. This verification config does is it holds those constraints and we first what you do is transform it into um something that can be used by the hub. You can see the hub takes this verification config and gives you an identifier. So it's a deterministic identifier for the config and you can use this later on to do different kinds of proofs for this config in the future. and the constructor where you set your verification config and verification ID.
And there's also since this is going to inherit self-verification route, there's a couple of methods you have to also implement. One is the custom verification hook. What this does is once a proof gets verified you will get access to um the disclosed outputs which is going to be the name ID number nationality and so on. If it's if it's um not disclosed it's going to be an empty array like an empty string but if it's disclosed then you can work on top of that ID. So this gives you a bunch of parameters.
So there's the destination chain ID and user identifier and the user definfined data. So what you can do is you can return different types of verification configs based on let's say different types of data. You're working on top of user defined data and if user defined data was let's say one well it's um it's a bite of one then use one kind of verification config. let's say return a config that checks for above 18 and otherwise you can return a config that is something else. So that is one way of using userdefined data and it it doesn't hold you to just one verification config and you can choose different ones based like dynamically.
Uh that is all you have to do in the contract. So there's the constructor and there's the custom verification hook that um gets called when the proof gets verified and you basically have the choice to execute some code after some verification like think of it as a call back and there's the get config ID function that um you can dictate the protocol to do a particular kind of verification based on these constraints. What are these constraints? Um you can see when we create and when we create the contract um inside the script and you can see there's a deploy proof of human. So what constraints are we setting here?
Since there's like forbidden countries that only has the United States um one forbidden country inside the set which is the United States which is the same as the front end and um there's also the minimum age 18 which is similar to what we set in the front end again. So these these two um constraints have to be the same because it's something you're trying to enforce um on chain and yeah we go on to create the um proof of human contract and you can um do discloses on it. So we can um deploy this contract real quick. So if you go to the env you can you'll see there's like a bunch of stuff bunch of env that you have to so I'm going to copy this into the env folder and this is something I loaded just before um the thing and we we don't need a API key. We can we are not going to verify a contract.
This this is how the contracts work. Like this is all you need to deploy the contract. And if if I just deploy the dot scripts, you can see that it's compiling and then your contract is going to be deployed eventually. Yeah. So until it gets deployed, we can um take a look at what the script does.
Script and deploy proof of human. So what we're doing is compiling and um deploying but deploying the contract and um we are checking the scope seed. So one thing about the scope seed is that it's the scope like I said the scope is an identifier for your application and it's a hash of your contract address and um your contract address as well as a seed you can um choose. So in our case the the seed we chose was um self workshop here. So this is also going to be similar to in what you have in your front end which is here.
So you can see it uses the same soft workshop if it's not defined of course but it's the same and you can see our contract got deployed at this address. So we can go ahead and use this inside our QR code. So the QR code expects an endpoint which is going to be your contact address. So we can switch over to here um and this is here. This is the contract we just deployed.
And we can run the app now and do a disk. I that npm and so you can see that there's a QR code over here and what this has what this contains information what information contains is basically your the the config you set for your protocol as well as um along with the config it has data about your contract address and so on. So I have a passport configured here. So you can see there's um multiple passports you can choose. And what I'll do is I will scan this QR code and I'll do a disclosure.
So you can see um there's a bunch of checks that have to happen before your disclosure happen. So yeah. So, I'm going to verify now. It's um and you can see it got verified. This is um a document that is is Indian.
You can see that um obviously I'm going to pass the checks because I'm not part of the United States. I can also change to a document. So we have mock documents inside self that you can create for developing and testing. So I have one document that is from the United States. Can um use that document and see if we can verify ourselves inside a protocol or not.
Select this document. Let's wait until this happens. It's now asking us to verify. Hold to verify. So if I hold this, um yeah, you can see the proof failed.
And actually the proof could never get created in the first place because you're part of this list of countries that you're not supposed to be a part of. So that proof actually never even got to your contracts. So basically can't create a proof in the first place. And that that would be it for the contracts example. So so one thing about the scope seed is you can head over to tools dot self.
xyz XYZ and you can and you can um generate the scope for your application by so for example the scope seed is self workshop and the endpoint is this. So make sure to use the check sum address actually um what anything that's consistent from the front end and back end. And you can see this would be similar to your um application. I cleared those logs but you can um you can see inside the script what it does is it sets the scope again. So because the scope depends on the contract address we can't set it in the first try.
You have to deploy your contract get the contract address and then and then um set the scope again. So you can see our proof of human contract has a set scope method where you can set the new scope. So this is precisely what this is. You can either do this use tools. XYZ or you can do what this script does and um let me see where that is.
Yeah. So what this script would do is use the library and hash it with the check summed address and the scope and it would lock the same um um number. And what you can do is to your contract set set the scope to the um number you get either from this or from the tools.xyz XYZ website and yeah you can also see the docs for how backend verification works. So if you head over here you can see it's so the front end part remains the same but you also have a new backend verifier that you can um set the configs for.
So all of this is same. You know what scope is the this scope would be the scope seed. you know the endpoint which is the URL of your um API along with like the path like resource path whether you are using mock passports the allowed ids and what this config storage is basically a store for all your configs and what you can do is you can store multiple configs inside the storage and use them based on a user defined userdefined data or um the yeah So how would you use this inside an API? So once so you can see here that inside your config store you can set different configs. So there's a strict verification let's say for um minimum of 21 age and there's standard verification for minimum of 18.
And um you can let's head over to quick start and see how it uses it there. So this is creating the config store like the backend verifier and you would set up an endpoint that would receive these four inputs. So the addestation ID, proof, public signals and the user context data and you would call the self backend verifier with these parameters and you can work through the exact same data type as essentially this call back. So it's essentially the same as this truck. You can work on top.
So it's it's your choice if you want to use offchain or onchain verification. And yeah, that would be it for backend. It's so backend is a bit more simpler than contracts. So that would be it for our dry run. And yeah, so going back to our slides, you can see uh yeah, so that was that was the code examples and thank you for attending this workshop.
So we we are excited to see you in ETH New Delhi and what projects you will be creating using self. You can go to our website or start us on GitHub and thank you. [Music]
Automatic transcript — names and jargon may be misspelled.