Your app contract needs to inherit the BrevisApp abstract contract. You can manually copy the required contracts from the quickstart repo, or install Brevis contracts via
yarnaddbrevis-contracts
TokenTransfer.sol
```solidity// SPDX-License-Identifier: MITpragmasolidity ^0.8.18;import"@openzeppelin/contracts/access/Ownable.sol";import"./lib/BrevisApp.sol";// Accept both ZK- and OP-attested results.contractTokenTransferisBrevisApp, Ownable {eventTransferAmountAttested(uint64 blockNum, address account, uint256 volume);bytes32public vkHash;constructor(address_brevisRequest) BrevisApp(_brevisRequest) Ownable(msg.sender) {}// BrevisQuery contract will call our callback once Brevis backend submits the proof.// This method is called with once the proof is verified.functionhandleProofResult(bytes32_vkHash,bytescalldata_circuitOutput) internaloverride {// We need to check if the verifying key that Brevis used to verify the proof// generated by our circuit is indeed our designated verifying key. This proves// that the _circuitOutput is authenticrequire(vkHash == _vkHash,"invalid vk"); (address accountAddr,uint64 blockNum,uint256 volume) =decodeOutput(_circuitOutput);emitTransferAmountAttested(blockNum, accountAddr, volume); }functiondecodeOutput(bytescalldata o) internalpurereturns (address,uint64,uint256) {uint64 blockNum =uint64(bytes8(o[0:8]));address userAddr =address(bytes20(o[8:28]));uint256 volume =uint256(bytes32(o[28:60]));return (userAddr, blockNum, volume); }// vkHash represents the unique circuit app logicfunctionsetVkHash(bytes32_vkHash) externalonlyOwner { vkHash = _vkHash; }}
Checking the Verifying Key Hash
Notice that we have a vkHash storage variable in the contract:
bytes32public vkHash;
By setting this variable to our previously computed vkhash, when Brevis calls our app contract, we can check the proof, that Brevis has verified before calling our contract, is indeed generated by our circuit.
Handling Proof Result
handleProofResult is where you process the computation result output from your circuit. There are two steps we must do before we use the proven data:
Check if the param _vkHash matches our stored vkHash. This verifies the identity of the circuit which produced the computation result.
Decode the param _circuitOutput.
The output values in your circuit definition are packed in the form of abi.encodePacked(...). The order of the variables is the same as the order you call the output APIs in the circuit.
If you are following this example to build the project, you can deploy this contract. If you are just doing research and would like to see a deployed version, see the deployed contract on Sepolia.