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 Attestations requires an API key that must be secured
2

Frontend Integration

Embed the Predicate Attestation into the transaction object

Mental model

  • With specific transaction information, your backend requests the PredicateAPI for an attestation.
  • The PredicateAPI returns a message detailing if the request is compliant and a corresponding attestation.
  • Your frontend includes that message when calling a predicated contract function.

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';

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, chainId } = req.body;
    
    try {
        // Encode the private function that is invoked by the predicated function
        const data = packFunctionArgs("sendCoin(address,uint256)", functionArgs);
        
        const request: PredicateRequest = {
            from: from,
            to: to,
            data: data,
            msg_value: '0',
            chain_id: chainId
        };

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

        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 in order to be invoked. You can see this commented in the Complete Integration Example

Next Steps

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