Before Any Integration

Configure Your Policy: Before integrating Predicate, you must deploy a policy or use an existing one. The policy ID must be stored in the ServiceManager contract.

Access the Network: The Predicate labs entity maintains a free service (middleware) which has been optimized for broadcasting tasks and aggregating signatures from the Operator Network.

Bridge Integration

This sample bridge contract inherits the Predicate Client, a library which provides necessary functions to enforce the corresponding policy.

One such function is authorizeTransactions, which verifies that the signatures nested in the authorization parameters belong to registered Predicate Operators.

Bridge.sol

import {PredicateClient} from "@predicate/mixins/PredicateClient.sol";

contract Bridge is PredicateClient {
    function dispatch(
        uint32 destinationDomain,
        bytes32 recipientAddress,
        bytes calldata messageBody,
        bytes calldata metadata,
        PredicateMessage calldata predicateMessage
    ) public payable virtual returns (bytes32) {

        // Require transaction authorization via the Predicate client
        bytes memory encodedSigAndArgs = abi.encodeWithSignature("_dispatch(uint32,bytes32,bytes,bytes)", receiver, amount);
        require(_authorizeTransaction(predicateMessage, encodedSigAndArgs), "MetaCoin: unauthorized transaction");

        return _dispatch(destinationDomain, recipientAddress, messageBody, metadata);
    }
}

Before users submit a transaction to the above contract, the interface must fetch signatures from the Predicate Network. Each Operator in the Predicate Network enforces and signs the transaction referencing the latest policy set by the application owner (see the Predicate ServiceManager contract).

To fetch these signatures, the front end makes an API call to the Predicate Middleware, which returns the signatures alongside the transaction. The following is an example of how this is done.

frontend.js
import * as sdk from 'predicate-sdk'

const predicate = new sdk.PredicateClient({
    apiUrl: PREDICATE_API_URL,
    apiKey: PREDICATE_API_KEY,
});

const functionArgsPacked = sdk.packFunctionArgs("_dispatch(uint32,bytes32,bytes,bytes)",
    [
        destinationDomain,
        recipientAddress,
        messageBody,
        metadata
    ]);

const predicateSignatures = await predicate.evaluatePolicy({
    from: wallet.address,
    to: contractAddress,
    data: functionArgsPacked,
    value: "0",
});

const predicateMessage = sdk.signaturesToBytes(result.data);

const tx: ethers.TransactionResponse = await contract[
    "dispatch(uint32,bytes32,bytes,bytes,(string,uint256,address[],bytes[]))"
    ](destinationDomain,
    recipientAddress,
    messageBody,
    metadata,
    predicateMessage);

await tx.wait()