Documentation Index
Fetch the complete documentation index at: https://docs.predicate.io/llms.txt
Use this file to discover all available pages before exploring further.
After deploying your contract and adding it to the dashboard, update your offchain integration to target your contract address and pass attestations onchain.
Update API requests
Replace verification_hash with to (your contract address).
Pass attestations to your contract
Include the attestation struct when calling predicated functions.
Backend Changes
Replace verification_hash with your contract address in the to field.app.post('/api/predicate/attestation', async (req, res) => {
const { userAddress, contractAddress } = req.body;
const response = await fetch('https://api.predicate.io/v2/attestation', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.PREDICATE_API_KEY,
},
body: JSON.stringify({
to: contractAddress,
from: userAddress,
chain: 'base',
}),
});
const result = await response.json();
if (!result.is_compliant) {
return res.status(403).json({ error: 'Not compliant' });
}
res.json({ attestation: result.attestation });
});
Request Parameters
| Field | Description |
|---|
to | Your deployed contract address |
from | The user’s wallet address |
chain | Chain name (e.g., base, ethereum, arbitrum) |
Frontend Changes
Fetch the attestation and pass it to your contract function.// 1) Fetch attestation from your backend
const { attestation } = await fetch('/api/predicate/attestation', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userAddress: account,
contractAddress: VAULT_ADDRESS,
}),
}).then(r => r.json());
// 2) Call your contract with the attestation
const tx = await vault.deposit(
{
uuid: attestation.uuid,
expiration: attestation.expiration,
attester: attestation.attester,
signature: attestation.signature,
},
{ value: depositAmount }
);
Attestation Struct
| Field | Type | Description |
|---|
uuid | bytes16 | Unique identifier for replay protection |
expiration | uint256 | Unix timestamp when the attestation expires |
attester | address | Address of the Predicate attester that signed |
signature | bytes | ECDSA signature over the attestation |
Backend Changes
Update your API calls to target your program address.app.post('/api/predicate/attestation', async (req, res) => {
const { userPubkey, programId, data } = req.body;
const response = await fetch('https://api.predicate.io/v2/attestation', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.PREDICATE_API_KEY,
},
body: JSON.stringify({
to: programId,
from: userPubkey,
data,
msg_value: '0',
chain: 'solana',
}),
});
const result = await response.json();
if (!result.is_compliant) {
return res.status(403).json({ error: 'Not compliant' });
}
res.json({ attestation: result.attestation });
});
Frontend Changes
Include an Ed25519 verification instruction before your program instruction, then call your method.const { attestation } = await fetch('/api/predicate/attestation', { /* ... */ })
.then(r => r.json());
// 1) Verify Ed25519 signature (must precede your program instruction)
const ed25519Instruction = Ed25519Program.createInstructionWithPublicKey({
publicKey: attesterPubkey.toBytes(),
message: messageHash,
signature: signatureBytes,
});
// 2) Create your program instruction
const clientInstruction = await program.methods
.yourMethod(attestation)
.accounts({
// ... your accounts
predicateRegistry: registryPda,
attesterAccount: attesterPda,
policyAccount: policyPda,
usedUuidAccount: usedUuidPda,
instructionsSysvar: SYSVAR_INSTRUCTIONS_PUBKEY,
})
.instruction();
// 3) Build and send (Ed25519 instruction MUST come first)
const transaction = new Transaction();
transaction.add(ed25519Instruction);
transaction.add(clientInstruction);
await provider.sendAndConfirm(transaction, [userKeypair]);