Onchain Integration

Onchain Integration

The heart of the Succinct protocol is the Succinct Gateway. The Succinct Gateway is the onchain entrypoint to all onchain proof verifications and callbacks in the Succinct protocol. Applications developed with the Succinct Platform should use the Succinct Gateway to utilize proofs in the onchain part of their application.

Succinct Gateway

The Succinct Gateway is a simple onchain entrypoint to all onchain proof verifications and callbacks in the Succinct protocol. The contract code can be found here (opens in a new tab) and an interface for the contract can be found here (opens in a new tab).

Function Id Registry

The Gateway maintains a mapping (opens in a new tab) of "function ids" (bytes32 unique identifiers) to the address of verifier smart contracts. To register a verifier with the Gateway, deploy your onchain verifier for your circuit, and use the registerFunction method (opens in a new tab) with the verifier's address and owner address. This call will return the functionId identifier of the deployed verifier.

For users of the Succinct Platform, when you perform a "deployment" on the platform, the transaction will deploy your verifier contract and register it with the Gateway, providing you with a functionId to use.

Integrate with Succinct Gateway

To integrate with the Succinct Gateway, consuming smart contracts have 2 options:

  • onchain requests: proofs are requested onchain and the request is stored in the gateway. Once a proof is generated and sent to the gateway, it will verify the proof and call a callback method on the consuming contract with the proof output and stored context from the originating request.
  • offchain requests: proofs are requested offchain along with a desired external call to a consuming smart contract. Once a proof is generated and sent to the gateway, it will verify the proof and call the provided external call on a consuming smart contract, which uses the verifiedCall method on the gateway to get the input/output of the proof.

Onchain Requests

For submitting requests for proofs onchain, you should use the requestCallback function of the gateway. A simple example can be found here (opens in a new tab).

Specify the functionId for which you've deployed a verifier, the input bytes for generating the proof, context bytes that will be passed during callback, the callback selector that the Gateway will call once the proof is verified, and the callback gas limit.

function requestCallback(
    bytes32 functionId,
    bytes memory input,
    bytes memory context,
    bytes4 callbackSelector,
    uint32 callbackGasLimit
) external payable returns (bytes32);

Your callback selector should have the following signature:

function handleCallback(bytes memory _output, bytes memory _context) external {
    // Ensure that this method can only be called by the gateway when it is in callback mode
    if (msg.sender != GATEWAY || !ISuccinctGateway(GATEWAY).isCallback()) {
        revert NotFromSuccinctGateway(msg.sender);
    }
    // FILL IN: Decode the output and context and handle the callback
}

How it works: When using requestCallback, the Gateway will store your request and emit an event that will trigger proof generation with the provided input bytes. Once a valid proof is generated, the Succinct relayer will automatically call the fulfillCallback (opens in a new tab) method on the gateway, which will use the stored request data to verify the request has not been previously fulfilled, verify the proof using the deployed verifier, and call your callback method with the output and context bytes.

Offchain Requests

If you want to submit requests for proofs offchain, use the Succinct API's request method by specifying the function id, input bytes, and callback information, like this:

curl "https://alpha.succinct.xyz/api/request/new" \
  --header "Authorization: Bearer <YOUR API KEY>" \
  --data-raw '{
        "chainId": 1, // Chain id of the destination chain
        "to": "0x123...", // address of the callback contract
        "data": "0x456...", // data for the callback
        "functionId": "0x55b...", // functionId
        "input": "0x123...." // input bytes
    }'

Upon recieving this request, the Succinct Platform will generate a proof corresponding to the circuits associated with your function id with the provided input bytes, and then call the fulfillCall method on the Gateway with the input and output bytes and the generated proof.

Solidity Integration

To use the verified inputs and outputs in your smart contract, one should use the verifiedCall function in SuccinctGateway.

function verifiedCall(bytes32 functionId, bytes memory input)
    external
    view
    returns (bytes memory);

verifiedCall can be thought of similarly to the .call method in Solidity, but with the user specifiying the functionId and input bytes and getting back the verified output bytes. The Gateway will ensure that the provided input and output have a valid proof that validates the function, when run with the input bytes, results in the provided output bytes.

A simple example of using verifiedCall in a smart contract can be found here (opens in a new tab) in our ZK Tendermint light client:

/// @notice Steps the light client from a trusted block to the next block.
function step(uint64 _trustedBlock) external {
    // Get an already stored header onchain that is trusted.
    bytes32 trustedHeader = blockHeightToHeaderHash[_trustedBlock];
    if (trustedHeader == bytes32(0)) {
        revert TrustedHeaderNotFound();
    }

    uint64 nextBlock = _trustedBlock + 1;
    if (nextBlock <= latestBlock) {
        revert TargetBlockNotInRange();
    }

    bytes memory input = abi.encodePacked(_trustedBlock, trustedHeader);

    // Call gateway to get the proof result.
    // The gateway will ensure that there is a valid proof for the provided functionId, input bytes and output bytes.
    // In this case, there is a valid proof that enough validators from the trusted block signed the next block.
    // And the output bytes of the circuit is the header hash of this next block.
    bytes memory output = ISuccinctGateway(gateway).verifiedCall(
        stepFunctionId, // The functionId of the verifier for the "step" circuit
        input
    );

    // Read the new header from request output.
    bytes32 newHeader = abi.decode(output, (bytes32));

    blockHeightToHeaderHash[nextBlock] = newHeader;
    latestBlock = nextBlock;

    emit HeadUpdate(nextBlock, newHeader);
}

How it works: When you send a proof request to the Succinct API (alongside specified callback information), Succinct will generate the proof using the provided input bytes and then call the fulfillCall (opens in a new tab) method on the Gateway with the input and output bytes and the generated proof. The Gateway will verify the proof for the provided functionId and then sets storage variables (opens in a new tab) with the functionId and input and output bytes. Then it executes a call to the provided callback address with the callback data (from the API request). During the callback execution, the callback contract uses the verifiedCall (opens in a new tab) method which simply checks that the provided functionId and input bytes match the stored storage variables and returns the output bytes.

Contract Addresses

The Succinct protocol is deployed as non-upgradeable contracts on the following chains:

SuccinctGateway

ChainChain IDAddress
Mainnet10x6c7a05e0ae641c6559fd76ac56641778b6ecd776 (opens in a new tab)
Goerli50x6c7a05e0ae641c6559fd76ac56641778b6ecd776 (opens in a new tab)
Sepolia111551110x6c7a05e0ae641c6559fd76ac56641778b6ecd776 (opens in a new tab)
Holesky170000x6c7a05e0ae641c6559fd76ac56641778b6ecd776 (opens in a new tab)
Gnosis1000x6c7a05e0ae641c6559fd76ac56641778b6ecd776 (opens in a new tab)
Arbitrum421610x6c7a05e0ae641c6559fd76ac56641778b6ecd776 (opens in a new tab)
Arbitrum Sepolia4216140x6c7a05e0ae641c6559fd76ac56641778b6ecd776 (opens in a new tab)
Base84530x6c7a05e0ae641c6559fd76ac56641778b6ecd776 (opens in a new tab)
Base Sepolia845320x6c7a05e0ae641c6559fd76ac56641778b6ecd776 (opens in a new tab)
Scroll5343520x6c7a05e0ae641c6559fd76ac56641778b6ecd776 (opens in a new tab)
Scroll Sepolia5343510x6c7a05e0ae641c6559fd76ac56641778b6ecd776 (opens in a new tab)

SuccinctFeeVault

ChainChain IDAddress
Mainnet10x296666e937b270193B960a7cEC526B351F353166 (opens in a new tab)
Goerli50x296666e937b270193B960a7cEC526B351F353166 (opens in a new tab)
Sepolia111551110x296666e937b270193B960a7cEC526B351F353166 (opens in a new tab)
Holesky170000x296666e937b270193B960a7cEC526B351F353166 (opens in a new tab)
Gnosis1000x296666e937b270193B960a7cEC526B351F353166 (opens in a new tab)
Arbitrum421610x296666e937b270193B960a7cEC526B351F353166 (opens in a new tab)
Arbitrum Sepolia4216140x296666e937b270193B960a7cEC526B351F353166 (opens in a new tab)
Base84530x296666e937b270193B960a7cEC526B351F353166 (opens in a new tab)
Base Sepolia845320x296666e937b270193B960a7cEC526B351F353166 (opens in a new tab)
Scroll5343520x296666e937b270193B960a7cEC526B351F353166 (opens in a new tab)
Scroll Sepolia5343510x296666e937b270193B960a7cEC526B351F353166 (opens in a new tab)

Non-Canonical Chain Contract Deployment

If there is not a canonical Succinct Gateway deployed by the Succinct team on a specific chain, it is possible to deploy the SuccinctGateway yourself. You can still use the Succinct platform for generating proofs, but then you will be responsible for relaying the proofs to the Gateway by yourself. This is an advanced mode of usage, so please reach out to us if you are interested in this.

SuccinctGateway Contracts

To deploy the contracts, you can follow these steps in the succinctx monorepo:

Register Circuits with your deployed Succinct Gateway

To generate proofs for your circuits, you will need to register your verifier contracts with the SuccinctGateway.

  1. Download the FunctionVerifier.sol contract from the release(s) corresponding to the circuits you want to generate proofs for. You can do this by clicking on the file in the "Build Files" section.

    Downloading FunctionVerifier.sol from Build Files

  2. Deploy and register the verifier contract(s) with your deployed Succinct Gateway. There are two ways to do this:

    a. Use cast commands to deploy FunctionVerifier.sol, then register it on the gateway.

    1. Deploy the verifier contract:

      forge create FunctionVerifier.sol:FunctionVerifier --private-key <YOUR_PRIVATE_KEY> --rpc-url <YOUR_RPC_URL> --etherscan-api-key <YOUR_ETHERSCAN_API_KEY> --verify
    2. Then register the verifier contract with the Gateway.

      • GATEWAY_ADDRESS is the address of your deployed Succinct Gateway.
      • OWNER is the address that will be the owner of the verifier contract (can be the same as the deployer). If you set this to 0x00..00, then the function ID is not upgradeable.
      • VERIFIER_ADDRESS is the address of the deployed verifier contract from the previous step.
      • CREATE2_SALT is the salt used for the create2 address of the verifier contract. This can be any bytes32 value (ex. 0xaa..aa).
      cast send <GATEWAY_ADDRESS> "registerFunction(address,address,bytes32)" <OWNER> <VERIFIER_ADDRESS> <CREATE2_SALT> ---private-key <YOUR_PRIVATE_KEY> --rpc-url <YOUR_RPC_URL>

    b. Use the Forge script in the succinctx repo. (No need to do this if you used the cast commands in the previous step.)

    1. In contracts/script/gateway-examples, there is an example script DeployAndRegisterFunction.s.sol that can be used to deploy and register a verifier contract with the Gateway here (opens in a new tab).

      Replace FunctionVerifier.sol with the verifier contract you want to deploy and add the following to contracts/.env.

      PRIVATE_KEY=<YOUR_PRIVATE_KEY>
      RPC_URL=<YOUR_RPC_URL>
      ETHERSCAN_API_KEY=<YOUR_ETHERSCAN_API_KEY>
      SUCCINCT_GATEWAY=<YOUR_GATEWAY_ADDRESS>
      CREATE2_SALT=<YOUR_CREATE2_SALT>
    2. Then run the following command to deploy and register the verifier contract with the Gateway:

      forge script script/gateway-examples/DeployAndRegisterFunction.s.sol --private-key <YOUR_PRIVATE_KEY> --rpc-url <YOUR_RPC_URL> --broadcast --verify --verifier etherscan --etherscan-api-key <YOUR_ETHERSCAN_API_KEY>