Skip to content Skip to footer

Workshop Task 1

Task 1: Confidential Transactions

okay let’s go so task number one confidential transaction  

I prepared the demo statler  repository  

Task 1: Getting Started -cloning the repo-

Let’s look at it. Go to github.com/oasisprotocol/demo-starter okay  so this is a mono repo. We have two packages, one is the back end and one is the front end.  

The back end is there with the contract itself and tools to deploy the contract we will use hardhat. 

For those not familiar,  hardhat is a smart contract management tool written in typescript in JavaScript. You can use hardhat to deploy your contract,  to run tests for example,  to combine the private key of your accounts used for testing, and then that private key that account will be then used in actual tests or to deploy your smart contract and similar.  The front end part we we’ll look at a little later,   is the browser you know part to access your data so let’s go ahead and let’s clone this repository  

There’s a Code button here just go ahead and copy it and I will use my command line git

clone and this repository 

okay so  let’s look let’s look at the contracts.

There is one contract called the message box  and the extension is sol

which means the solidity language and  this message box contract looks like this

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract MessageBox {

    string public message;

    address public author;

    function setMessage(string calldata in_message) external {

        message = in_message;

        author = msg.sender;

    }

}

so there’s a public message stored inside the contract and the author of this message and the only function is the message you want to store and the message  variable inside the on chain will now contain your message and the author field on the Chain will store the sender of this message so when you submit a transaction you also provide the public key  of the transaction and sign it with your key

That’s actually how the distributed Network knows which fees to take from

and who is the center of the transaction  This message dot Sender is  just a syntactic  solidity integer  which gives you access to the address of the sender so the address is derived from the public key  who signed the transaction  

This is the contract we are going to use we’re going to deploy and play with and  the next one 

Okay, we will need  metamask.  I suggest you open your browser and if you haven’t yet you go ahead and install the metamask extension so head to https://www.metamask.io let’s download it to Chrome 

Let’s create a new wallet you will need to come up with a password and let’s not do any backup for now

Awesome, okay let’s pin in the browser so we have a nice button here at the top so this is our metamask and  you can see that it also generated  one account for us. This is  the address of our account  ZX denominates that it will be a hex value emV is the the actual address so if you go to here you can see the full address of our account  and this is not related to ethereum you know metamask says it’s connected to ethereum mainet but basically  the account corresponds to key pair which is stored inside your metamask extension inside your browser so you can simply switch the networks and you will have the same key pairs the same account  regardless of which network you use. 

We will need to add the sapphire test net because we’ll be using that to deploy our contract so let’s go ahead and add it  

One way is to edit manually but I’m  lazy so I will head over to our documentation where we already prepared a magic button you just click on and then the network is added so had to create dApp to sapphire and then you have this Magic Buttons here you just add so we will use test net for now  and I will add it so this is the chain ID the currency symbol

https://docs.oasis.io/dapp/sapphire/

To obtain test tokens for the Oasis Sapphire testnet Oasis provides what is called a faucet. Using a faucet is a common way to receive test tokens for development and testing purposes without needing to purchase them on the mainnet.

Once you’ve correctly accessed the faucet at faucet.testnet.oasis.dev and submitted your funding request by providing your Metamask wallet address. After completing the verification step to confirm you’re not a robot, the request should be processed, and you should see the test tokens deposited into your account shortly.

If you’ve entered the correct wallet address and completed the verification successfully, you should expect to see the tokens appear in your account momentarily. If you encounter any issues or delays, double-check the address you provided and ensure that the verification process is completed accurately.

Once the tokens arrive, you can proceed with testing and development on the Oasis Sapphire testnet. Remember to exercise caution and avoid deploying any production services or real assets on the testnet, as it is subject to periodic wipes and may experience unexpected issues

oh there they are cool all right

Next Step  alright so now we have test tokens now we have our account we will deploy our contract to sapphire testnet  so let’s go line by line.

First we need to  go to our backend folder and type in the pnpm install command which downloads all all the dependencies from the network from the internet and then we will run pmpm build

This build step will  compile our solidity smart contract written in the solidity language to the bytecode. The bytecode will then be executed by the ethereum virtual machine  inside  sapphire  and it will also produce some other things like the ABI the application binary interface this is like a list of all the functions your dApp or your browser can access  and call 

let’s go so  I’m already in the backend folder this is okay and let’s see and run pnpm install this will download all the dependencies 

I have pnpm build okay pnpm build

It works so if we look at what happened  inside the contracts folder we have our solidity contract and then this abi folder was generated which contains  this binary interface maybe we just look at it so we can see that this set message function is here it doesn’t return anything  it accepts one string. This is something your  dApp compilers then take and  produce a nice interface in typescript that you just type in like my message box to set message and all the magic then happens behind the scenes. The transaction gets generated, it gets broadcast, it’s waited then for a while and so on. 

Hardhat config file: (in the future the github will be linked) 

import { promises as fs } from ‘fs’;

import path from ‘path’;

import ‘@nomicfoundation/hardhat-ethers’;

import ‘@oasisprotocol/sapphire-hardhat’;

import ‘@typechain/hardhat’;

import canonicalize from ‘canonicalize’;

import {JsonRpcProvider} from “ethers”;

import ‘hardhat-watcher’;

import { TASK_COMPILE } from ‘hardhat/builtin-tasks/task-names’;

import { HardhatUserConfig, task } from ‘hardhat/config’;

import ‘solidity-coverage’;

const TASK_EXPORT_ABIS = ‘export-abis’;

task(TASK_COMPILE, async (_args, hre, runSuper) => {

  await runSuper();

  await hre.run(TASK_EXPORT_ABIS);

});

task(TASK_EXPORT_ABIS, async (_args, hre) => {

  const srcDir = path.basename(hre.config.paths.sources);

  const outDir = path.join(hre.config.paths.root, ‘abis’);

  const [artifactNames] = await Promise.all([

    hre.artifacts.getAllFullyQualifiedNames(),

    fs.mkdir(outDir, { recursive: true }),

  ]);

  await Promise.all(

    artifactNames.map(async (fqn) => {

      const { abi, contractName, sourceName } = await hre.artifacts.readArtifact(fqn);

      if (abi.length === 0 || !sourceName.startsWith(srcDir) || contractName.endsWith(‘Test’))

        return;

      await fs.writeFile(`${path.join(outDir, contractName)}.json`, `${canonicalize(abi)}\n`);

    }),

  );

});

// Unencrypted contract deployment.

task(‘deploy’)

  .setAction(async (args, hre) => {

    await hre.run(‘compile’);

    // For deployment unwrap the provider to enable contract verification.

    const uwProvider = new JsonRpcProvider(hre.network.config.url);

    const MessageBox = await hre.ethers.getContractFactory(‘MessageBox’, new hre.ethers.Wallet(accounts[0], uwProvider));

    const messageBox = await MessageBox.deploy();

    await messageBox.waitForDeployment();

    console.log(`MessageBox address: ${await messageBox.getAddress()}`);

    return messageBox;

});

// Read message from the MessageBox.

task(‘message’)

  .addPositionalParam(‘address’, ‘contract address’)

  .setAction(async (args, hre) => {

    await hre.run(‘compile’);

    const messageBox = await hre.ethers.getContractAt(‘MessageBox’, args.address);

    const message = await messageBox.message();

    const author = await messageBox.author();

    console.log(`The message is: ${message}, author: ${author}`);

  });

// Set message.

task(‘setMessage’)

  .addPositionalParam(‘address’, ‘contract address’)

  .addPositionalParam(‘message’, ‘message to set’)

  .setAction(async (args, hre) => {

    await hre.run(‘compile’);

    let messageBox = await hre.ethers.getContractAt(‘MessageBox’, args.address);

    const tx = await messageBox.setMessage(args.message);

    const receipt = await tx.wait();

    console.log(`Success! Transaction hash: ${receipt!.hash}`);

  });

// Hardhat Node and sapphire-dev test mnemonic.

const TEST_HDWALLET = {

  mnemonic: “test test test test test test test test test test test junk”,

  path: “m/44’/60’/0’/0”,

  initialIndex: 0,

  count: 20,

  passphrase: “”,

};

const accounts = process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : TEST_HDWALLET;

const config: HardhatUserConfig = {

  networks: {

    hardhat: { // https://hardhat.org/metamask-issue.html

      chainId: 1337,

    },

    ‘sapphire’: {

      url: ‘https://sapphire.oasis.io’,

      chainId: 0x5afe,

      accounts,

    },

    ‘sapphire-testnet’: {

      url: ‘https://testnet.sapphire.oasis.dev’,

      chainId: 0x5aff,

      accounts,

    },

    ‘sapphire-localnet’: { // docker run -it -p8545:8545 -p8546:8546 ghcr.io/oasisprotocol/sapphire-localnet -test-mnemonic

      url: ‘http://localhost:8545’,

      chainId: 0x5afd,

      accounts,

    },

  },

  solidity: {

    version: ‘0.8.16’,

    settings: {

      optimizer: {

        enabled: true,

        runs: 200,

      },

      viaIR: true,

    },

  },

  watcher: {

    compile: {

      tasks: [‘compile’],

      files: [‘./contracts/’],

    },

    test: {

      tasks: [‘test’],

      files: [‘./contracts/’, ‘./test’],

    },

    coverage: {

      tasks: [‘coverage’],

      files: [‘./contracts/’, ‘./test’],

    },

  },

  mocha: {

    require: [‘ts-node/register/files’],

    timeout: 50_000,

  },

};

export default config;

Inside the artifact  we also have data for contract verification  so if you want to deploy a contract and you want to verify it then this is the file you need to upload to sourcify service or ether scan and so on.

Sapphire is not currently on ether scan but supposedly for any contract verification Services out there  this artifacts contracts folder is  the place where you want to look for this verification material right okay so the contract is now compiled  

let’s deploy it

The deployment instructions are here.

 There’s an environmental variable called private key where we will need to put the private key of our account so our account in metamask we will need to export that and then the command is npx hardhat deploy and on 

which network

Sapphire test net okay let’s see so let’s open our metamask and let’s

export the private key 

Account Details show Private key okay okay right this is our private key 

Okay so let’s try it out, private key equals x and our hex value

of the private key and then the npx hardhat deploy which network well sapphire test net

This is the address (595Cc) where our contract was deployed to and now if you want to communicate with our contract this is the address that we need to provide each time  and we can now launch our Explorer and see if this is really the case 

If we go to explorer.oasis.io and type in the address it says that there are no results found on mainnet which is expected but there is one result on another network  and it’s on test oh that’s nice and if you click on the the address you get some nice info like who who  created the contract the V account so is this really our account x v yeah  and  what else so there is one

transaction related to this contract the contract creation transaction obviously  it does not store any tokens any nfts, there are not token transfers yet no events okay and this transaction which created the contract is here it’s in plain format it’s not encrypted you know see this  gray padlock here  and yeah from two is shown here and the amount the fee which we needed to pay for the contract creation the amount of gas we used

 okay cool

Task 1: Confidential Transactions

What’s with this format plane?

This is oasis sapphire specific. This is not anywhere inside ethereum. It is the end to end encryption I talked about previously so the contract creation  transaction in our case was not encrypted but now let’s look how the encrypted transactions would look because the end to end encryption is something special in that regard.

You need to have a part of the key on your side you have  the other part of the key on the other side and when you combine the keys together  you get the shared secret used to encrypt or decrypt the transaction and the interesting thing is that with neither two pairs, the secret key will never travel across the network so no man of the middle attacks will be possible.  

How is this achieved?

On the client side, for example, in our command line interface or inside the metamask  you generate a key pair and then  you request from the web3 Gateway the  paratime public key which you will use so this is the other part of the the key which you will use to execute the transaction and this is important because if you are connected to a malicious web  Gateway

it can inject you with not the paratime public key and you would encrypt it you know in a good  sense that this is confidential but the web three Gateway could exploit this and  steal your data so this is why you should always run your own node and your own web3 gateway.

 okay so you encrypt and submitted the transaction  to your web three  Gateway and the the gate relays that transaction to the  compute node and then the compute node  requests  the private key from the key manager, combines it with the public key with your public key you generated yourself and then it  deres the shared secret used to decrypt your transaction and then the computation is performed, the results computed and the encrypted  result is returned back to you through through the same channel and then you use your  secret part of your

of your client key pair to decrypt the result.

This sounds pretty complicated  but  actually it’s not that difficult. 

The only thing from the developer point of view you need to do is you import our Sapphire hardhat npm package and it automatically wraps the ethers  Library  and whichever  call you do afterwards  it will be encrypted  and signed, 

This is specifically for hardhat if you are developing front- end app then you won’t use hardhat there  you would use the standard connector and  you you will use  this wrap  function we provide

Put your your provider there usually this is metamask or Brave or something and it will get  wrapped and any  communication afterwards will be automatically  encrypted

okay so now let’s see  how  setting the message  and retrieving the message  works. This is already part of the demo project I will just demonstrate what the code looks like and then we will run  the command to set the message and retrieve the message https://github.com/oasisprotocol/demo-starter 

https://github.com/oasisprotocol/demo-starter/blob/main/backend/hardhat.config.ts

This is the hardhat config file actually  but something hardhat offers and it’s pretty nice to use are so-called tasks so one tasks we already used is the deploy task and it’s defined

here so what it does is it compiled the contract it connected to  the  sapphire test net in in our case because this is what we we passed in the Network  parameter it got the message box typescript  bindings and called the deploy function  on that smart contract object and it used our unwrapped provider the UW in this case is unwrapped provider this this means this is the original you know PL text provider  it’s not using encryption and then when the

message box was deployed. It just waited a few moments and showed the address.

Now let’s say we want to set message this task here  so this message,  this task will expect the address this is the contract address we are connecting to and the message content will compile the contract,  it will obtain the existing contract so it will not deploy the contract but just connect to an existing contract and it will call this set message transaction but in this case  we will use this get contract at we will not inject any special special  providers as we did here we will just use the original  WRAp provider as it is and this set message transaction here should be encrypted.

Then we just wait for the transaction finish and show the transaction hash okay let’s try it out

Previously we used this private key equals something npx hard deploy now we will use setMessage and then we will need to provide the contract address this one and the message itself  I will say like Hello World from the sapphire 101 Workshop

okay and now hopefully the transaction will pass and if everything works fine it should also pop up  on the Explorer side.

The transaction seem to pass oh here it is look so this transaction went through and it’s a contract call kind of transaction and it’s encrypted the format is encrypted and if you look at the data itself.

If you take the ordinary ethereum transaction you should see something which is decodable inside the aski hello world from the sapphire workshop and here it’s  encrypted using you know

the the key I mentioned before okay  let’s move on

I retrieve the message well there is this function called message

work okay awesome so this is the stored message hello world from the sapphire one-oh-one  and the author is ourselves okay

We transmitted an encrypted transaction congratulations!  

Now the question is how private is this? 

If you watch closely in the Explorer there’s the ‘from:’ and ‘to:’ field which cannot be encrypted because the computer needs to know which smart contract it needs to communicate with so you cannot hide that. 

The gas price or the gas used is also important data because you will get your gas refunded and it’s pretty obvious how much gas  the contract call will use and for this reason this is an important attack Vector  we also provide the gas padding calls inside of the sapphire pre-compiles so you can just  pad gas up front to a specific amount and then from the external Observer view they cannot know which contract call did you exactly you know call because it’s just the address of the contract but not inside the contract what did you call  because the gas

is used the for any contract call out there for example if you program it this way  and of course the payload is also known, the size of the contract call so if you have one function which

has like ten parameters and the other function which has has no parameters then of course the payout of the first transaction will will be much bigger and you cannot find it basically I mean you then need to prepare that each function will have the same  signature in that regard  

Task 1: Smart Privacy

Now we learned that there are confidential and non-confidential transactions and  the task for us is, “Can we use the unencrypted transaction to set the message as well?” because as I mentioned earlier you there are cases where you want to publicly announce that there is some transaction out there which you transmitted and everyone should be able to see it and decode it  and the answer is 

-yes you can do that and basically we’ll just take the  code from the deploy task here. If we do this and paste it here It will  use the the unencrypted version 

let’s try, Here we go so there seems to be another transaction and this transaction should be plain. Here it is so welcome this is this is smart privacy and now you can you know decode this transaction and it should clearly say that yeah well hello world from Sapphire 101 it’s you know obvious here that this is a string it’s not some random binary blob all right  so  we’re a bit short on time so I’ll just move forward to task two