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
SuccinctFeeVault
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:
git clone
thesuccinctx
monorepo: https://github.com/succinctlabs/succinctx (opens in a new tab)- In the
contracts
directory, follow the instructions (opens in a new tab) to install Foundry, and runforge build
to build the contracts - Follow the instructions here (opens in a new tab) for deploying the contracts to a new chain.
Register Circuits with your deployed Succinct Gateway
To generate proofs for your circuits, you will need to register your verifier contracts with the SuccinctGateway.
-
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. -
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.-
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
-
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.)-
In
contracts/script/gateway-examples
, there is an example scriptDeployAndRegisterFunction.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 tocontracts/.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>
-
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>
-