Solidity contract for verifying SEDA Fast API responses on-chain using secp256k1 signatures.
The FastVerifier contract allows smart contracts to verify that data returned by the SEDA Fast API was genuinely signed by SEDA's trusted execution environment. This enables low-latency oracle data consumption without waiting for full consensus.
# Clone
git clone https://github.com/sedaprotocol/seda-evm-fast-verification.git
cd seda-evm-fast-verification
# Install dependencies
forge install
npm installCopy the example environment file and configure:
cp .env.example .envRequired variables:
| Variable | Description |
|---|---|
SEDA_FAST_SIGNER |
SEDA Fast API signer address |
PRIVATE_KEY |
Deployer private key (for deployment) |
BASE_SEPOLIA_RPC_URL |
RPC endpoint |
BASESCAN_API_KEY |
For contract verification |
Get the public key from the SEDA Fast API and derive the address:
# Get public key from SEDA Fast API
curl https://fast-api.seda.xyz/info | jq -r '.publicKey'
# Derive address
node -e "console.log(require('ethers').computeAddress('0x<public-key>'))"forge buildforge test -vv-
Get testnet ETH for Base Sepolia from a faucet
-
Get a Basescan API key from basescan.org/apis
-
Configure
.env:cp .env.example .env
SEDA_FAST_SIGNER=0x593CEBb17C116D48d69b108711f2D8C419ed8758 PRIVATE_KEY=0x...your-private-key... BASE_SEPOLIA_RPC_URL=https://sepolia.base.org BASESCAN_API_KEY=your-api-key
# Deploy and verify (reads PRIVATE_KEY from .env automatically)
forge script script/FastVerifier.s.sol:FastVerifierScript \
--rpc-url base_sepolia \
--broadcast \
--verify \
-vvvvAdd the network to foundry.toml:
[rpc_endpoints]
base_sepolia = "${BASE_SEPOLIA_RPC_URL}"
base_mainnet = "${BASE_MAINNET_RPC_URL}"
arbitrum_sepolia = "${ARBITRUM_SEPOLIA_RPC_URL}"
[etherscan]
base_sepolia = { key = "${BASESCAN_API_KEY}", url = "https://api-sepolia.basescan.org/api" }
base_mainnet = { key = "${BASESCAN_API_KEY}", url = "https://api.basescan.org/api" }
arbitrum_sepolia = { key = "${ARBISCAN_API_KEY}", url = "https://api-sepolia.arbiscan.io/api" }Then deploy:
forge script script/FastVerifier.s.sol:FastVerifierScript \
--rpc-url <network_name> \
--broadcast \
--verifyIf automatic verification fails, verify manually:
forge verify-contract <DEPLOYED_ADDRESS> src/FastVerifier.sol:FastVerifier \
--chain base-sepolia \
--constructor-args $(cast abi-encode "constructor(address)" $SEDA_FAST_SIGNER) \
--etherscan-api-key $BASESCAN_API_KEYTest deployment without broadcasting:
forge script script/FastVerifier.s.sol:FastVerifierScript \
--rpc-url base_sepolia \
-vvvvUse the verification script to test a deployed contract with a SEDA Fast API response.
Save the Fast API response to a file and verify:
[...]
{
"data": {
"dataResult": {
"drId": "b37491d3969dc300eb8313234c73fd695d83ff351f3d2ec8fda62c00d2c95b14",
"gasUsed": "45768976025625",
"blockHeight": "0",
"blockTimestamp": "1767018307922",
"consensus": true,
"exitCode": 0,
"version": "0.0.1",
"result": "7b22...",
"paybackAddress": "",
"sedaPayload": ""
},
"signature": "c414c3de2b367c36..."
}
}
[...]
# Verify against deployed contract
node scripts/verify.js <CONTRACT_ADDRESS> "$(cat response.json)"π Verification Details:
Contract: 0xbaa298897006f5707676354a6325930d368352a2
DR ID: 0xb37491d3969dc300eb8313234c73fd695d83ff351f3d2ec8fda62c00d2c95b14
Timestamp: 2025-12-29T14:25:07.922Z
Consensus: true
Exit Code: 0
Expected Signer: 0x593CEBb17C116D48d69b108711f2D8C419ed8758
β
VALID signature
interface IFastVerifier {
/// @notice Verifies a SEDA Fast API response signature
/// @param result The data result from the Fast API
/// @param signature The secp256k1 signature (65 bytes)
/// @return isValid True if signature is valid
function verify(
SedaDataTypes.Result calldata result,
bytes calldata signature
) external view returns (bool isValid);
/// @notice Verifies and returns the result data
/// @dev Reverts if signature is invalid
function verifyAndGetResult(
SedaDataTypes.Result calldata result,
bytes calldata signature
) external view returns (bytes memory);
}import {FastVerifier} from "./FastVerifier.sol";
import {SedaDataTypes} from "@seda-protocol/evm/contracts/libraries/SedaDataTypes.sol";
contract MyContract {
FastVerifier public verifier;
constructor(address _verifier) {
verifier = FastVerifier(_verifier);
}
function consumeOracleData(
SedaDataTypes.Result calldata result,
bytes calldata signature
) external {
// Verify and get the result in one call
bytes memory data = verifier.verifyAndGetResult(result, signature);
// Decode and use the data
// ...
}
}The SedaDataTypes.Result struct matches the SEDA Fast API response:
struct Result {
bytes32 drId; // Data Request ID
uint128 gasUsed; // Gas consumed
uint64 blockHeight; // Block height (0 for Fast API)
uint64 blockTimestamp; // Unix timestamp in milliseconds
bool consensus; // Consensus reached
uint8 exitCode; // Execution exit code
string version; // Protocol version ("0.0.1")
bytes result; // The actual result data
bytes paybackAddress; // Payback address (usually empty)
bytes sedaPayload; // SEDA payload (usually empty)
}- The contract verifies signatures against a trusted signer address set at deployment
- Uses OpenZeppelin's battle-tested ECDSA library
- Handles both standard (v=27/28) and compact (v=0/1) signature formats
MIT