LogoLogo
HomeExplorerGitHub
  • 🗂️Introduction
    • 🧐5Ws-1H about CESS
    • ✨Technical Highlight
    • 🔨Use Cases
    • 📜Whitepaper
    • 💬Contact & Social Media
  • ⛏️CESS Nodes
    • Consensus Nodes
      • Running a Consensus Node
      • Reward Mechanism
    • Storage Nodes
      • Running a Storage Node
      • Running MultiNodes
      • Node Troubleshooting
      • Storage Miner Upgrade Guide
    • CDN Nodes
      • Running a Cacher
      • Running a Retriever
    • TEE Nodes
      • What is TEE Node
      • Running a TEE Node
  • 🌏Community Members
    • Ambassador Program
    • Community Governance
  • 🔥CESS User Guide
    • CESS Account
    • Testnet Faucet
    • Territory Operation
  • 💻Developers
    • CESS SDK
      • Golang SDK
        • Preface
        • Install
        • Initialization
        • Properties
        • Data Process
        • Object/File
          • StoreFile
          • StoreObject
          • RetrieveFile
          • RetrieveObject
          • StoreFileToMiners
          • RetrieveFileFromMiners
        • Chain Related
          • Audit
            • QueryChallengeSnapShot
            • QueryCountedClear
            • QueryCountedServiceFailed
            • SubmitIdleProof
            • SubmitServiceProof
            • SubmitVerifyIdleResult
            • SubmitVerifyServiceResult
          • Babe
            • QueryAuthorities
          • Balances
            • QueryInactiveIssuance
            • QueryTotalIssuance
            • TransferToken
          • CessTreasury
            • QueryCurrencyReward
            • QueryEraReward
            • QueryReserveReward
            • QueryRoundReward
          • DeOSS
            • Authorize
            • CancelAuthorize
            • DestroyOss
            • QueryAllOss
            • QueryAuthorityList
            • QueryOss
            • RegisterOss
            • UpdateOss
          • FileBank
            • QueryAllRestoralOrder
            • QueryUserHoldFileList
            • QueryUserFidList
            • QueryDealMap
            • QueryFile
            • QueryRestoralOrder
            • CalculateReport
            • CertIdleSpace
            • ClaimRestoralNoExistOrder
            • ClaimRestoralOrder
            • DeleteFile
            • GenerateRestoralOrder
            • PlaceStorageOrder
            • ReplaceIdleSpace
            • RestoralOrderComplete
            • TransferReport
            • UploadDeclaration
            • TerritoryFileDelivery
          • SchedulerCredit
            • QueryCurrentCounters
          • Session
            • QueryValidators
          • Sminer
            • IncreaseCollateral
            • IncreaseDeclarationSpace
            • MinerExitPrep
            • MinerWithdraw
            • QueryAllMiner
            • QueryAllRestoralTarget
            • QueryCompleteSnapShot
            • QueryCounterForMinerItems
            • QueryExpenders
            • QueryMinerItems
            • QueryPendingReplacements
            • QueryRestoralTarget
            • QueryRewardMap
            • QueryStakingStartBlock
            • ReceiveReward
            • RegisterPoisKey
            • RegnstkAssignStaking
            • RegnstkSminer
            • UpdateBeneficiary
            • UpdateSminerEndPoint
          • Staking
            • QueryAllBonded
            • QueryAllNominators
            • QueryCounterForValidators
            • QueryCurrentEra
            • QueryEraValidatorReward
            • QueryErasRewardPoints
            • QueryErasTotalStake
            • QueryLedger
            • QueryNominatorCount
            • QueryValidatorCommission
            • QueryValidatorsCount
            • QueryeErasStakers
            • QueryeNominators
          • StorageHandler
            • QueryPurchasedSpace
            • QueryTotalIdleSpace
            • QueryTotalServiceSpace
            • QueryUnitPrice
            • QueryTerritory
            • QueryConsignment
            • MintTerritory
            • ExpandingTerritory
            • RenewalTerritory
            • ReactivateTerritory
            • TerritoryConsignment
            • CancelConsignment
            • BuyConsignment
            • CancelPurchaseAction
          • System
            • QueryAccountInfo
            • QueryAccountInfoByAccountID
            • QueryBlockNumber
          • Tee
            • QueryAllWorkers
            • QueryEndpoints
            • QueryMasterPubKey
            • QueryWorkerAddedAt
            • QueryWorkers
          • RPC Calls
            • ChainGetBlock
            • ChainGetBlockHash
            • ChainGetFinalizedHead
            • NetListening
            • SystemChain
            • SystemProperties
            • SystemSyncState
            • SystemVersion
        • Toolset
      • Javascript SDK
      • Rust SDK
        • Preface
        • chain
          • Audit
            • Challenge Snapshot
            • Counted Clear
            • Counted Service Failed
            • Submit Idle Space Proof
            • Submit Service Proof
            • Submit Verify Idle Result
            • Submit Verify Service Result
          • File Bank
            • Bucket
            • Clear User List
            • deal Map
            • File
            • Restoral Order
            • User Bucket List
            • User Hold File List
            • Calculate Report
            • Certify Idle Space
            • Claim Restoral Noexist Order
            • Claim Restoral Order
            • Create Bucket
            • Delete Bucket
            • Delete File
            • Generate Restoral Order
            • Replace Idle Space
            • Restoral Order Complete
            • Territory File Delivery
            • Transfer Report
            • Upload Declaration
          • Oss
            • Authority List
            • Oss
            • Authorize
            • Cancel Authorize
            • Destroy
            • Register
            • Update
          • Storage Handler
            • Consignment
            • Pay Order
            • Purchased Space
            • Territory Expired
            • Territory Frozen Counter
            • Territory Frozen
            • Territory
            • Territory Key
            • Total Power
            • Total Space
            • Unit Price
            • Buy Consignment
            • Cancel Consignment
            • Cancel Purchase Action
            • Create Order
            • Expand Territory
            • Mint Territory
            • Reactivate Territory
            • Renew Territory
            • Territory Consignment
            • Territory Grants
            • Territory Rename
    • Smart Contract
      • Issue ERC20
      • Deploy an ink! Smart Contract
      • Deploy a Solidity Smart Contract
      • Build a Proof of Existence dApp (ink!)
      • Build a NFT Marketplace (ink!)
      • Build a Proof of Existence dApp (Solidity)
    • Advanced Guides
      • Substrate and EVM Address Conversion
      • Commonly Used Libs
      • CESS Code Overview
  • 📱CESS Products
    • DeOSS
      • Introduction
      • Technical Highlights
      • Architecture
      • Data Access Process
      • API Description
        • Prerequisites
        • Identity Signature
        • Upload File/Object
        • Range Upload
        • Download File
        • Preview File
        • Delete File
        • View File Metadata
        • View Version
  • 📚References
    • 📚Node Operations
      • Running RPC Node
    • 📚In-depth Technical Features
      • R²S
      • RRC
      • LBSS
    • Data Lifecycle
      • Data Upload
      • Data Download
      • Data Deletion
      • Data Restore
      • Data Audit
    • Distributed Storage
      • Identification
      • Consistency Guarantee
      • Node Discovery
      • Message Protocol
      • Storage Method
    • DePIN
      • The DePIN Portrait
    • AI
      • CESS: The Future of Decentralized Data Infrastructure for the AI-Driven World
      • Decentralized Solutions for AI: How CESS is Shaping a Trusted Future
      • CESS in CES: Insights from Jensen Huang’s CES 2025 Speech and CESS Network’s Role in the AI Era
      • CESS AI Agent Hub: The Intelligent Portal of the AI Times
  • 📖Glossary
Powered by GitBook
On this page
  • Objective
  • Smart Contract (Solidity)
  • Prerequisites
  • Development
  • Front End
  • Prerequisites
  • Development
  • Tutorial Completion
  • References

Was this helpful?

Edit on GitHub
  1. Developers
  2. Smart Contract

Build a Proof of Existence dApp (Solidity)

Last updated 1 year ago

Was this helpful?

Objective

In this tutorial, we will walk through the whole process of building a full dApp, writing both the smart contract in Solidity and the front end in React. Again, we will be making the Proof of Existence App. For those unfamiliar with what Proof of Existence on blockchain is or have yet to go through the previous tutorial. Please take a look at the Proof of Existence .

Let's first look at the smart contract (Solidity) side and then the front-end side.

Smart Contract (Solidity)

Prerequisites

mkdir hardhat
cd hardhat
pnpm dlx hardhat init

You will be asked a series of questions on configuring the project.

✔ What do you want to do? · Create a TypeScript project
✔ Hardhat project root: · (your chosen dir)
✔ Do you want to add a .gitignore? (Y/n) · y
✔ Do you want to install this sample project's dependencies with npm (hardhat @nomicfoundation/hardhat-toolbox)? (Y/n) · y

Hardhat default to be using npm package manager, but feel free to use your preferred package manager.

pnpm install -D hardhat-deploy

By default, Hardhat places a Lock.sol smart contract inside hardhat/contracts directory. Check that everything works by running pnpm hardhat test and see all test cases pass.

Development

  1. Let's configure hardhat.config.ts so it can deploy smart contracts to a hardhat network, a locally running hardhat node, and a locally running cess node.

    Please use the following hardhat config in hardhat.config.ts

    const config: HardhatUserConfig = {
      solidity: "0.8.19",
      defaultNetwork: "hardhat",
      namedAccounts: {
        deployer: {
          default: 0,
        },
        beneficiary: {
          default: 1,
        },
      },
      networks: {
        hardhat: {
          // issue: https://github.com/sc-forks/solidity-coverage/issues/652,
          // refer to : https://github.com/sc-forks/solidity-coverage/issues/652#issuecomment-896330136
          initialBaseFeePerGas: 0
        },
        localhost: {
          url: "http://localhost:8545",
          accounts: ["0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"],
        },
        "cess-local": {
          url: "http://localhost:9944", // RPC endpoint of CESS testnet
          chainId: 11330,
          // private key of `//Alice` from Substrate
          accounts: ["0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a"],
        }
      }
    };
  2. Now, for the smart contract, we want to have the following methods:

    • claim(bytes32 hash): a method for the caller to claim ownership of a file with the specified hash.

    • forfeit(bytes32 hash): a method for the caller to forfeit ownership of a file with the specified hash.

    • ownedFiles(): retrieving all the file hashes owned by the user.

    • hasClaimed(hash): check whether a file hash has been claimed yet.

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.19;
    
    contract ProofOfExistence {
      mapping(bytes32 => address) public files;
      mapping(address => bytes32[]) public users;
    
      event Claimed(address indexed owner, bytes32 indexed file);
      event Forfeited(address indexed owner, bytes32 indexed file);
    
      error NotFileOwner();
      error FileAlreadyClaimed();
    
      modifier isOwner(bytes32 hash) {
        address from = msg.sender;
        if (files[hash] != from) revert NotFileOwner();
        _;
      }
    
      modifier notClaimed(bytes32 hash) {
        address from = msg.sender;
        if (files[hash] != address(0)) revert FileAlreadyClaimed();
        _;
      }
    
      function hasClaimed(bytes32 hash) public view returns (bool) {
        address owner = files[hash];
        return (owner != address(0));
      }
    
      function ownedFiles() public view returns (bytes32[] memory) {
        address from = msg.sender;
        return users[from];
      }
    
      function claim(bytes32 hash) public notClaimed(hash) returns (bool) {
        address from = msg.sender;
    
        // update storage files
        files[hash] = from;
    
        // update storage users
        bytes32[] storage userFiles = users[from];
        userFiles.push(hash);
    
        emit Claimed(from, hash);
        return true;
      }
    
      function forfeit(bytes32 hash) public isOwner(hash) returns (bool) {
        address from = msg.sender;
    
        // update storage files
        delete files[hash];
    
        // locate the index of the file going to be deleted.
        bytes32[] storage userFiles = users[from];
        uint32 delIdx = 0;
        for (uint32 i = 0; i < userFiles.length; i++) {
          if (userFiles[i] == hash) {
            delIdx = i;
            break;
          }
        }
        // update storage users by swap-delete
        if (delIdx != userFiles.length - 1) {
          userFiles[delIdx] = userFiles[userFiles.length - 1];
        }
        // delete
        userFiles.pop();
    
        emit Forfeited(from, hash);
        return true;
      }
    }
    • Run a CESS node locally.

      1. A CESS signing account, we will be using /Alice: cXjmuHdBk4J3Zyt2oGodwGegNFaTFPcfC48PZ9NMmcUFzF6cc. This account also has some TCESS tokens initialized in a local CESS node.

      2. The EVM-mapped account of the above CESS signing account, which is 0xd43593c715fdd31c61141abd04a99fd6822c8558

      3. An EVM signing account, we will import the Alice private key 0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a into Metamask, and this yields an address of 0x8097c3C354652CB1EEed3E5B65fBa2576470678A.

      4. The Substrate-mapped address of the above EVM signing account (#3), which is cXgEvnbJfHsaN8HfoiEWfAi4QBENYbLKitRfG1CDYZpKTRRuw.

    • With tokens in #3 wallet, we can deploy the Solidity smart contract on the local CESS node. We run the following hardhat command. Here, please modify the hardhat.config.ts accordingly about the account that you use for deployment.

      pnpm hardhat deploy --reset --network cess-local

      Take note of the deployed address for ProofOfExistence smart contract.

Front End

Prerequisites

If you run it, you will see the Proof of Existence (Solidity) widget in the bottom right corner:

Development

We won't go over the front-end code line by line, but there are a few features I will point out here.

  • const RPC_ENDPOINT = "http://localhost:9944";
    
    const cessLocal = {
      id: 11330,
      name: "CESS Local",
      network: "cess-local",
      nativeCurrency: {
        decimal: 18,
        name: "CESS Testnet Token",
        symbol: "TCESS",
      },
      rpcUrls: {
        public: { http: [RPC_ENDPOINT] },
        default: { http: [RPC_ENDPOINT] },
      },
    };
  • Then, we build up the chain's public clients and pass the clients in createConfig(). Finally, the config object is passed into the WagmiConfig React component.

    const { publicClient, webSocketPublicClient } = configureChains(
      [cessLocal],
      [
        jsonRpcProvider({
          rpc: (chain) => ({
            http: RPC_ENDPOINT,
          }),
        }),
      ],
    );
    
    const config = createConfig({
      publicClient,
      webSocketPublicClient,
    });
    
    //...
    export default function PoESolidityWithWagmiProvider(props) {
      return (
        <WagmiConfig config={config}>
          <PoESolidity />
        </WagmiConfig>
      );
    }
    • use useDebounce() to ensure the smart contract is not being called too often.

    • use usePrepareContractWrite() to prepare for the contract write action.

    • use useContractWrite() to get the write() function to be passed in our TxButton component click handler.

    • use useWaitForTransaction() to get the transaction information and its status.

Tutorial Completion

Congratulation! Let's recap what we have done in this tutorial:

  • We have successfully implemented a PoE logic in the Solidity smart contract and deployed it on a local CESS node.

  • Starting with the Substrate Front End Template and adding wagmi React hook library, we have successfully implemented the front end that interacts with the smart contract.

References

The complete source code of this tutorial can be seen at our .

We will use as the smart contract development toolchain. So, let's install and initialize hardhat library.

Let's also add library to help deploy and manage the deployed smart contracts.

If you have any issues, refer back to the , its , and .

A local hardhat node listen to , while a local CESS node listens to port. With this config and hardhat-deploy component, we can deploy smart contract to our locally running CESS node by pnpm hardhat deploy --network cess-local.

We can now deploy the smart contract in our local CESS node, following the tutorial of .

Having the four accounts and their addresses ready. For details, refer to .

We first transfer some test tokens (say 1M tokens) from account #1 (Substrate account) to account #3 (EVM account) in .

Next, we can connect to the smart contract using and interact with the smart contract.

Install

Install

Install

Run a local development chain of the CESS node because the front end will connect to the local CESS chain. Refer here on .

The complete front-end source code can be seen .

The major development of the frontend is at frontend/src/ProofOfExistenceSolidity.js, as shown .

We are using library for the React Hooks to work with Ethereum smart contracts.

To use wagmi, we define our CESS local chain :

To connect to our Ethereum wallet, we use hook, which is being called in the .

We get the information of the currently selected account and its balance by and hooks. These hooks are being used in the .

We read the contract data using . One point to note here is we need to specify the account argument so the msg.sender value is set on the smart contract side.

For writing to the contract, we follow the practice . We:

Now, you can build your dApps and deploy them on the CESS testnet to test it out. If you haven't done so, try also to as well.

💻
cess-examples repository
Smart Contract
Front end
Hardhat
hardhat-deploy
hardhat directory
package.json
hardhat.config.ts
http://localhost:8545
http://localhost:9944
Deploying a Smart Contract in CESS
Substrate EVM Address Conversion
Polkadot.js Apps
Remix
Git
Node v18
pnpm
here
here
wagmi
at here
useConnect()
ConnectWallet component
useAccount()
useBalance()
PoESolidity component
useContractRead()
outlined in wagmi docs
develop a dApp with Ink! smart contract
Hardhat
hardhat-deploy
CESS Node
Substrate Front End Template
wagmi
Solidity
Prompting for Hardhat Project
Front-end Template PoE-Solidity
background introduction
how to run a local CESS chain