# Create a Schema Hook

In this step, we will extend the verifier contract generated by the ZK library and implement the [ISPHook ](https://docs.sign.global/for-builders/index-1/index/index/index/isphook)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](https://docs.sign.global/for-builders/index-1/index/index/index/isphook) 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!
