Smart Contracts
Select client contract
Use BasicPredicateClient for most integrations. Use PredicateClient only if your policy validates function calls, parameters, or value-based limits.
Inherit and implement
Your contract will inherit the selected client contract and implement the protected functions
Deploy your contract
Include the PredicateRegistry address and PolicyID in your deployment
Mental model
- Onchain, Predicate effectively serves as access control for your protected business logic
- Your contract(s), via
PredicateClient or BasicPredicateClient, requires each transaction to be accompanied with an attestation before executing protected business logic.
- If the attestation is valid and unexpired, execution proceeds; otherwise the transaction reverts.
Both PredicateClient and BasicPredicateClient contracts leverage ERC-7201 namespaced storage for upgrade safety and are audited.
”Predicated” functions
To gate a function behind Predicate, add an Attestation parameter and call _authorizeTransaction before your business logic. This function is provided by the PredicateClient and verifies that the caller has a valid, unexpired attestation signed by a registered Predicate attester.Any function that calls _authorizeTransaction is predicated — it will revert unless the caller provides a compliant attestation from the Predicate API.Deploying Your Contracts
A deployed contract is a prerequisite in order to complete onboarding to our dashboard
After implementing the Predicate Client (see examples below), you’ll need to set the Registry address and PolicyID.
_policyID: The policy identifier generated during onboarding from the predicate dashboard - you’ll get this in the next step.
_registry: The Predicate Registry address. See Supported Chains.
Installation
npm i @predicate/contracts
Examples
Use for who-based policies: AML/KYC, allowlist/denylist, geo-restrictions. No calldata encoding required — the API request and onchain verification only need from, to, and chain.// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {BasicPredicateClient} from "@predicate/contracts/src/mixins/BasicPredicateClient.sol";
import {Attestation} from "@predicate/contracts/src/interfaces/IPredicateRegistry.sol";
contract Vault is BasicPredicateClient, Ownable {
mapping(address => uint256) public balances;
event Deposit(address indexed user, uint256 amount);
constructor(
address _owner,
address _registry,
string memory _policyID
) Ownable(_owner) {
_initPredicateClient(_registry, _policyID);
}
/// @notice Deposit with Predicate authorization
function deposit(
Attestation calldata _attestation
) external payable {
require(_authorizeTransaction(_attestation, msg.sender), "Unauthorized");
balances[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}
// Admin functions
function setPolicyID(string memory _policyID) external onlyOwner {
_setPolicyID(_policyID);
}
function setRegistry(address _registry) external onlyOwner {
_setRegistry(_registry);
}
}
Use when your policy validates specific function calls, parameters, or value-based limits.// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {PredicateClient} from "@predicate/contracts/src/mixins/PredicateClient.sol";
import {Attestation} from "@predicate/contracts/src/interfaces/IPredicateRegistry.sol";
contract Vault is PredicateClient, Ownable {
mapping(address => uint256) public balances;
event Deposit(address indexed user, uint256 amount);
constructor(
address _owner,
address _registry,
string memory _policyID
) Ownable(_owner) {
_initPredicateClient(_registry, _policyID);
}
function deposit(
uint256 _amount,
Attestation calldata _attestation
) external payable {
// Encode the internal function being protected
bytes memory encodedSigAndArgs = abi.encodeWithSignature(
"_deposit(uint256)",
_amount
);
require(
_authorizeTransaction(_attestation, encodedSigAndArgs, msg.sender, msg.value),
"Vault: unauthorized transaction"
);
_deposit(_amount);
}
function _deposit(uint256 _amount) internal {
require(msg.value == _amount, "Vault: incorrect ETH amount");
balances[msg.sender] += _amount;
emit Deposit(msg.sender, _amount);
}
// Admin functions
function setPolicyID(string memory _policyID) external onlyOwner {
_setPolicyID(_policyID);
}
function setRegistry(address _registry) external onlyOwner {
_setRegistry(_registry);
}
}
Next Step: Dashboard Setup to configure your organization and policies.Solana Program Integration
Predicate provides an Anchor-based program for Solana that handles attestation validation onchain.Installation
git clone https://github.com/PredicateLabs/sol-contracts
cd sol-contracts
anchor build
Integration Overview
The Solana implementation uses Program Derived Addresses (PDAs) for:
- Policy Accounts: Policies are associated with program addresses
- Attestor Accounts: Registered attestors who can sign attestations
- Used UUID Accounts: Replay protection via PDA existence checks
Example Usage
// Call predicated instruction with attestation
invoke(
&validate_attestation(
registry_pda,
attestor_account,
policy_account,
target_program,
msg_value,
encoded_instruction_data,
attestor_key,
attestation,
),
&[/* accounts */],
)?;
// If validation succeeds, execute business logic
The Solana implementation is in active development and has not yet been audited. Use with caution in production environments.
Resources