Offchain Service Integration

For this walkthrough, we’ll use a web application as an example, though most other services require only a subset of these steps.
1

Backend Integration

Fetching Predicate Attesations requires an API key that must be secured
2

Frontend Integration

Embed the Predicate Attesation into the transaction object

Backend Integration

Since you’ll be serving a UI to end users, we recommend storing the API key and making Predicate API calls on your backend service:
Example Backend Proxy
import {PredicateClient, PredicateRequest, packFunctionArgs, signaturesToBytes} from '@predicate/core';
import { ethers } from 'ethers';

const predicateClient = new PredicateClient({
    apiUrl: 'https://api.predicate.io/',
    apiKey: process.env.PREDICATE_API_KEY!
});

// Create individual route that returns predicate attestation
app.post('/api/predicate/evaluate', async (req, res) => {
    const { from, to, functionArgs } = req.body;
    
    try {
        // Encode the private function that is invoked by the Predicate function
        const data = packFunctionArgs("_sendCoin(address,uint256)", functionArgs);
        
        const request: PredicateRequest = {
            from: from,
            to: to,
            data: data,
            msg_value: '0'
        };

        const evaluationResult = await predicateClient.evaluatePolicy(request);
        
        if (!evaluationResult.is_compliant) {
            return res.status(400).json({ 
                error: 'Transaction not compliant',
                result: evaluationResult 
            });
        }

        const predicateMessage = signaturesToBytes(evaluationResult);
        res.json({ predicateMessage });
        
    } catch (error) {
        res.status(500).json({ error: 'Failed to evaluate policy' });
    }
});

Frontend Integration

Use our SDK to nest the attestation details into the transaction object before the user is prompted to sign:
Frontend Implementation Snippet
// Fetch attestation from your backend
const response = await fetch('/api/predicate/evaluate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        from: userAddress,
        to: contractAddress,
        functionArgs: [receiverAddress, amount]
    })
});

const { predicateMessage } = await response.json();

// Submit transaction with predicate message
const tx = await contract.sendCoin(
    receiverAddress,
    amount,
    [
        predicateMessage.taskId,
        predicateMessage.expireByBlockNumber,
        predicateMessage.signerAddresses,
        predicateMessage.signatures
    ]
);

Understanding Predicate Functions

In both integration patterns, you’ll have a Predicated Function on your smart contract - this is the function that requires a ‘Predicate Message’ Attestation. The Predicate Function contains your protected business logic and ensures compliance before execution. You can see this commented in the Complete Integration Example

Next Steps

Contact the Predicate team for production API keys and higher rate limits!