# Create a Schema Hook

In this step, we will extend the verifier contract generated by the ZK library and implement the [ISPHook ](/for-builders/index-1/index/index/index/isphook.md)interface. From the previous step, our generated contract looks something like this:

```solidity
pragma solidity >=0.7.0 <0.9.0;

contract Groth16Verifier {
    ...

    function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[32] calldata _pubSignals) public view returns (bool) {
        ...
     }
 }
```

{% hint style="info" %}
In the generated verifier, change all instances of `calldata` to `memory`.
{% endhint %}

The generated verifier should now look like this:

```solidity
pragma solidity >=0.7.0 <0.9.0;

contract Groth16Verifier {
    ...

    function verifyProof(uint[2] memory _pA, uint[2][2] memory _pB, uint[2] memory _pC, uint[32] memory _pubSignals) public view returns (bool) {
        ...
     }
 }
```

To set up your schema hook, create another file that contains the following contents:

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

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

contract SHA256PreimageVerifier is Groth16Verifier, Ownable {
    address public spInstance;

    constructor() Ownable(_msgSender()) { }

    function setSPInstance(address instance) external onlyOwner {
        spInstance = instance;
    }
}

// @dev This contract implements the actual schema hook.
contract ZKHook is ISPHook, SHA256PreimageVerifier {
    error Unsupported();
    error ZKVerificationFailed();

    function didReceiveAttestation(
        address, // attester
        uint64, // schemaId
        uint64, // attestationId
        bytes calldata extraData
    )
        external
        payable
    {
        if (_msgSender() != spInstance) revert Unsupported();
        (uint256[2] memory _pA, uint256[2][2] memory _pB, uint256[2] memory _pC, uint256[32] memory _pubSignals) =
            abi.decode(extraData, (uint256[2], uint256[2][2], uint256[2], uint256[32]));
        // If the SHA256 preimage proof verification fails, revert.
        if (!verifyProof(_pA, _pB, _pC, _pubSignals)) revert ZKVerificationFailed();
    }

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

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

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

We begin by importing our generated `Groth16Verifier` contract, OpenZepplin packages, and the [ISPHook](/for-builders/index-1/index/index/index/isphook.md) contract.

Next, we create the `SHA256PreimageVerifier` contract which is responsible for setting the contract address for the Sign Protocol instance and adding ownable functionality.

Last, we create `ZKHook` to add all of our schema hook logic. Because ZK proofs can be large, we recommend making use of the `extraData` field - this data gets passed to Sign Protocol's contract when attestations are created or revoked but does not get stored onchain. `extraData` is only forwarded to schema hooks for data processing and verification, so this is a perfect scenario for providing required ZK-proof-related data. This hook decodes the inputted data and passes it to the verifier's `verifyProof()` function. The call reverts if verification fails. If verification succeeds, the attestation will be successfully created.

### Finishing Up

Once you have completed your ZK schema hook, it is time to deploy the contract to your desired network. After the contract gets deployed, [create a new schema](https://app.sign.global/create-schema) on the same network and set the hook smart contract address to the address of your deployed schema hook contract. Your ZK schema hook is ready to go!


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.sign.global/for-builders/advanced-topics/zk-attestations/create-a-schema-hook.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
