Telepathy
Search
K

Getting Started Example

Getting started with Succinct state queries
In this example, a dApp developer on L2 is writing a voting app and wants to gate voting by a User's ERC721 balance on Ethereum mainnet (L1). The developer can use Succinct's StateQuery protocol as follows to request this information on L2 and have the results available in a callback function specified by the developer.
StateQuery memory stateQuery = StateQuery({
chainId: 1, // Which chain to get information from
blockNumber: 20012312, // Which block to use
fromAddress: address(0), // The msg.sender of the query, usually irrelevant
toAddress: address(0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03), // Which contract to call
toCalldata: abi.encodeWithSelector(IERC721.balanceOf.selector, msg.sender)
});
IStateQueryGateway(stateQueryGateway).requestStateQuery(
stateQuery,
L2VotingOnChainRequest.continueVote.selector, // Which function to callback into with result
abi.encode(option, msg.sender) // What other data to pass to the callback
);
In the code snippet above, the developer requests for a state query on Ethereum mainnet that fetches the voter's NFT balance on the Noun's NFT contract. When requestStateQuery is called, the StateQueryGateway emits an event and the Succinct attestation network computes the result of the query (using the eth_call RPC) and attests to the result. After ~30 seconds, the attestations are aggregated, submitted and verified on-chain and the callback specified in the request is called with the result.
Putting it all together, here is a very simple example L2 voting contract that uses state queries.
contract L2VotingOnChainRequest {
address public STATE_QUERY_GATEWAY = address(0x1b132819aFE2AFD5b76eF6721bCCC6Ede40cd9eC);
address public addressERC721 = address(0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03);
uint32 public chainId = 1;
uint64 public snapshotBlock = 20012312;
mapping(address => uint256) addrToVote;
function vote(uint256 option) external {
require(option == 1 || option == 2, "Invalid option");
if (addrToVote[msg.sender] != 0) {
revert("Cannot vote twice");
}
StateQuery memory stateQuery = StateQuery({
chainId: chainId,
blockNumber: snapshotBlock,
fromAddress: address(0),
toAddress: addressERC721,
toCalldata: abi.encodeWithSelector(IERC721.balanceOf.selector, msg.sender)
});
IStateQueryGateway(STATE_QUERY_GATEWAY).requestStateQuery(
stateQuery,
L2VotingOnChainRequest.continueVote.selector, // Which function to call after async call is done
abi.encode(option, msg.sender) // What other data to pass to the callback
);
}
function continueVote(bytes memory _requestResult, bytes memory _callbackExtraData) external {
require(msg.sender == STATE_QUERY_GATEWAY);
uint256 balance = abi.decode(_requestResult, (uint256));
(uint256 option, address msgSender) = abi.decode(_callbackExtraData, (uint256, address));
if (balance >= 1) {
addrToVote[msgSender] = option;
}
}
}