COTI V2 Documentation
  • Welcome
  • Networks
    • Faucet
    • Contracts Addresses
    • Adding the COTI Network to Metamask
  • How COTI Works
    • Introduction
      • EVM Introduction
      • Conceptual Overview
      • Use Cases and Applications
      • COTI Architecture
    • Advanced Topics
      • Garbled Circuits
      • AES Keys
      • Precompiles
      • Whitepaper
      • COTI vs others
  • Build on COTI
    • Core Concepts
      • Account Onboarding Procedure
      • Private Data Types
      • Supported Operations on Private Data Types
    • Quickstart
    • Guides
      • Basic Private Smart Contract
      • Account Onboard
      • Sending a Transaction with Encrypted Inputs
      • Resolving a Transaction's Encrypted Outputs
      • Writing a Private Smart Contract
      • Dos and Don'ts
        • Proper Use of Types
        • No Constant/Immutable Secret Types
        • No Public Contract Variables
      • Best Practices
        • Careful Onboarding
        • Careful Decrypting
        • Don't loop over an array without an index
        • Check Overflow
    • Tools
      • TypeScript SDK
      • Ethers.js
      • Python SDK
      • Web3.py
      • Contracts Library
        • MPC Core
        • Data Privacy Framework
        • Tokens
          • Private ERC20
          • Private ERC721
        • Onboard
      • Hardhat
      • Remix Plugin
      • COTI MetaMask Snap
      • Developer Sandbox
  • Running a COTI Node
    • COTI Node Ecosystem Litepaper
  • COTI Bridge
    • Swap COTI V1 Funds to COTI V2
  • Support and Community
    • Glossary
    • Telegram
    • Discord
    • GitHub
    • X
    • YouTube
  • COTI Builders Program
Powered by GitBook
On this page

Was this helpful?

Edit on GitHub
  1. Build on COTI
  2. Guides

Basic Private Smart Contract

This guide will walk you through the process of taking a simple contract that tracks the sum of all numbers passed as arguments to the add function, and converting it into a privacy-enabled version.

Copy and paste the following smart contract into a new file Counter.sol:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

contract Counter {
    uint64 private _sum;
    
    function sum() public view returns (uint64) {
        return _sum;
    }

    function add(uint64 value) external {
        _sum += value;
    }
}

The next thing we will want to do is import the MPC Core library, which will help us make calls to the precompiled contracts that allow us to work with private data types.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import "@coti-io/coti-contracts/contracts/utils/mpc/MpcCore.sol";

contract Counter {
    uint64 private _sum;
    
    function sum() public view returns (uint64) {
        return _sum;
    }

    function add(uint64 value) external {
        _sum += value;
    }
}

Now we are ready to integrate private computations into our smart contract! To do this, we can change the types of _sum and value to their equivalent private data type (utUint64 and itUint64 respectively).

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import "@coti-io/coti-contracts/contracts/utils/mpc/MpcCore.sol";

contract Counter {
    utUint64 private _sum;
    
    function sum() public view returns (utUint64) {
        return _sum;
    }

    function add(itUint64 calldata value) external {
        _sum += value;
    }
}

If you would try to compile this smart contract, you will encounter a compilation error. This is because _sum and value are of different types and they do not support the standard Solidity arithmetic operators. Let's update the code so that it uses the appropriate methods provided to us by the MPC Core library.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import "@coti-io/coti-contracts/contracts/utils/mpc/MpcCore.sol";

contract Counter {
    utUint64 private _sum;
    
    function sum() public view returns (utUint64) {
        return _sum;
    }

    function add(itUint64 calldata value) external {
        gtUint64 value_ = MpcCore.validateCiphertext(value);
        gtUint64 sum_ = MpcCore.onBoard(_sum.ciphertext);
        
        sum_ = MpcCore.add(sum_, value_);
        
        _sum = MpcCore.offBoardCombined(sum_, msg.sender);
    }
}

This code will compile successfully, but if we were to deploy it and then try to call add, we would find that the transaction would be reverted. This is because by default, the storage slot used by the _sum state variable is initialized to zero. This causes an error when we try to onboard it into the gcEVM, since it was not encrypted using the network AES key. Let's fix the issue by setting the value of _sum inside of the constructor.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import "@coti-io/coti-contracts/contracts/utils/mpc/MpcCore.sol";

contract Counter {
    utUint64 private _sum;
    
    constructor() {
        gtUint64 sum_ = MpcCore.setPublic64(0);
        
        _sum = MpcCore.offBoardCombined(sum_, msg.sender);
    }

    function sum() public view returns (utUint64) {
        return _sum;
    }

    function add(itUint64 calldata value) external {
        gtUint64 value_ = MpcCore.validateCiphertext(value);
        gtUint64 sum_ = MpcCore.onBoard(_sum.ciphertext);
        
        sum_ = MpcCore.add(sum_, value_);
        
        _sum = MpcCore.offBoardCombined(sum_, msg.sender);
    }
}

Lastly, since values that are encrypted with the network AES key are not very useful to dApps and users, we will update the sum function to return only the value encrypted with the user's AES key.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import "@coti-io/coti-contracts/contracts/utils/mpc/MpcCore.sol";

contract Counter {
    utUint64 private _sum;
    
    constructor() {
        gtUint64 sum_ = MpcCore.setPublic64(0);
        
        _sum = MpcCore.offBoardCombined(sum_, msg.sender);
    }

    function sum() public view returns (ctUint64) {
        return _sum.userCiphertext;
    }

    function add(itUint64 calldata value) external {
        gtUint64 value_ = MpcCore.validateCiphertext(value);
        gtUint64 sum_ = MpcCore.onBoard(_sum.ciphertext);
        
        sum_ = MpcCore.add(sum_, value_);
        
        _sum = MpcCore.offBoardCombined(sum_, msg.sender);
    }
}
PreviousGuidesNextAccount Onboard

Last updated 5 months ago

Was this helpful?

In order to deploy the contract on the COTI network, you may consider using our or our .

Remix Plugin
Hardhat Template