LogoLogo
  • Sign Protocol
    • Introduction
    • FAQ
  • For Hackers
    • Getting Started
  • For Builders
    • Getting Started
      • Fundamentals
      • Tutorials
        • Building a Simple Notary Platform
          • Schema Creation
          • Attestation Creation
          • Querying Attestations
          • Parsing Attestation Data
        • Delegate Attestation Creation
          • Express Backend
          • Solidity
      • Examples
        • Attestation Discord Bot
        • KYC-Gated Smart Contract Access Control
    • Supported Networks
    • Sign Developer Platform
    • Advanced Topics
      • Cross Chain Attestations
      • Hybrid Attestations
      • Schema Hooks
        • Tutorial: Checking Attestation Data
          • Schema Creation
          • Schema Hook
          • Creating an Attestation (Solidity)
      • ZK Attestations
        • Compile a Circuit
        • Create a Schema Hook
    • Querying Data
      • NPM SDK
        • Usage & Examples
      • REST APIs
        • Schemas
        • Attestations
      • GraphQL
        • Schemas
        • Attestations
    • Writing Data
      • NPM SDK
        • Installation
        • Usage
          • Schemas
          • Attestations
        • Examples
        • Changelog
      • Smart Contract
        • EVM
          • Interfaces
            • ISP
            • ISPHook
            • IVersionable
          • Data Models
            • Schema
            • Attestation
            • DataLocation
  • For Thinkers
    • A Future Of Verifiable Trust
    • Effective Attestations
    • Incentive-Aligned Public Good
    • Glossary
      • Attestations
      • Schema
      • Schema Registry
      • Attestation Repository
  • Case Study
    • EthSign
    • KYC-Gated Contract Calls
    • Proof of Audit
    • Developer Onchain Reputation
    • Onboarding Web2 Data
Powered by GitBook
LogoLogo

Copyright Sign 2021-2024

On this page
  • Validation Logic
  • Schema Hook Implementation
  • Bringing It All Together

Was this helpful?

  1. For Builders
  2. Advanced Topics
  3. Schema Hooks
  4. Tutorial: Checking Attestation Data

Schema Hook

PreviousSchema CreationNextCreating an Attestation (Solidity)

Last updated 5 months ago

Was this helpful?

A schema hook contract is a regular solidity smart contract that inherits from the interface and implements all required functions. You can implement the ISPHook interface on any contract, allowing you to merge Sign Protocol schema callbacks with your application's smart contract logic.

For clarity in this tutorial, we will separate our schema hook contract from the contract containing validation logic.

Validation Logic

Our schema hook is responsible for ensuring attestation data contains a number above a certain threshold. If this condition is met (the threshold value in the attestation data is large enough), the attestation will be successfully created. Otherwise, we will revert the transaction and the attestation will fail to be created.

For our validator contract, we need to create two functions: setThreshold and _checkThreshold. The threshold specified in setThreshold will determine which attestations are allowed to be created. We will enforce this by calling _checkThreshold in our schema hook contract.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

// @dev This contract manages attestation data validation logic.
contract DataValidator is Ownable {
    uint256 public threshold;

    error NumberBelowThreshold();

    constructor() Ownable(_msgSender()) { }

    function setThreshold(uint256 threshold_) external onlyOwner {
        threshold = threshold_;
    }

    function _checkThreshold(uint256 number) internal view {
        // solhint-disable-next-line custom-errors
        require(number >= threshold, NumberBelowThreshold());
    }
}

Schema Hook Implementation

When an attestation is received, the schema hook's logic is pretty simple: we need to read the attestation data to extract the threshold value we are trying to validate, and then we need to validate it.

Reading Attestation Data

import { Attestation } from "@ethsign/sign-protocol-evm/src/models/Attestation.sol";
...
Attestation memory attestation = ISP(_msgSender()).getAttestation(attestationId);

Decoding Attestation Data

The attestation object you retrieved in the last step will have data encoded according to your schema's data format. In our case, the schema we created contains only a uint256. We can use abi.decode to decode the attestation data into the correct format.

abi.decode(attestation.data, (uint256));

You can now use this decoded data to call _checkThreshold.

If your schema has additional variables in its data field, these will be decoded similarly. For example, take the following schema data format:

[
    {
        "name": "name",
        "type": "string"
    },
    {
        "name": "timestamp",
        "type": "uint256"
    },
    {
        "name": "hash",
        "type": "bytes32"
    }
]

An attestation's data following the above data format can be decoded in Solidity using the following:

(string memory name, uint256 timestamp, bytes32 hash) = abi.decode(attestation.data, (string, uint256, bytes32));

Bringing It All Together

For this tutorial, we will only implement didReceiveAttestation, but the process is the same for the other functions in ISPHook. In our case, we did not check the attester, nor did we use the schemaId or extraData.

extraData is not the same as your attestation's data - this parameter provides a way for you to send additional information to your schema hook when an attestation is being created or revoked. extraData is not stored onchain with the attestation itself. It is only used in the schema hook.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { ISP } from "@ethsign/sign-protocol-evm/src/interfaces/ISP.sol";
import { ISPHook } from "@ethsign/sign-protocol-evm/src/interfaces/ISPHook.sol";
import { Attestation } from "@ethsign/sign-protocol-evm/src/models/Attestation.sol";

// @dev This contract manages attestation data validation logic.
contract DataValidator is Ownable {
    uint256 public threshold;

    error NumberBelowThreshold();

    constructor() Ownable(_msgSender()) { }

    function setThreshold(uint256 threshold_) external onlyOwner {
        threshold = threshold_;
    }

    function _checkThreshold(uint256 number) internal view {
        // solhint-disable-next-line custom-errors
        require(number >= threshold, NumberBelowThreshold());
    }
}

// @dev This contract implements the actual schema hook.
contract DataValidatorHook is ISPHook, DataValidator {
    error UnsupportedOperation();

    function didReceiveAttestation(
        address, // attester
        uint64, // schemaId
        uint64 attestationId,
        bytes calldata // extraData
    )
        external
        payable
    {
        Attestation memory attestation = ISP(_msgSender()).getAttestation(attestationId);
        _checkThreshold(abi.decode(attestation.data, (uint256)));
    }

    function didReceiveAttestation(
        address, // attester
        uint64, // schemaId
        uint64, // attestationId
        IERC20, // resolverFeeERC20Token
        uint256, // resolverFeeERC20Amount
        bytes calldata // extraData
    )
        external
        pure
    {
        revert UnsupportedOperation();
    }

    function didReceiveRevocation(
        address, // attester
        uint64, // schemaId
        uint64, // attestationId
        bytes calldata // extraData
    )
        external
        payable
    {
        revert UnsupportedOperation();
    }

    function didReceiveRevocation(
        address, // attester
        uint64, // schemaId
        uint64, // attestationId
        IERC20, // resolverFeeERC20Token
        uint256, // resolverFeeERC20Amount
        bytes calldata // extraData
    )
        external
        pure
    {
        revert UnsupportedOperation();
    }
}

Once this contract has been deployed, you can create your schema and set the appropriate contract address as your schema hook. Once you have created your schema with a schema hook, move to the next page where we will go over how to create an attestation in solidity.

To get the attestation data, we need to read the attestation object from Sign Protocol's contract. You can find all available Sign Protocol smart contract functions . Remember that your schema hook is being called from the Sign Protocol smart contract, so you can use _msgSender() to get the appropriate smart contract address that you need to call.

ISPHook
here