Documentation Index
Fetch the complete documentation index at: https://docs.cultura.xyz/llms.txt
Use this file to discover all available pages before exploring further.
This flow details how a digital asset progresses from its initial minted state (Level 0) to becoming attested (Level 1) and finally achieving Verified Rights (Level 2), making it licensable within the Cultura Proof of Rights Protocol.
Flow Diagram
Explanation
Part 1: Attestation (Level 0 -> Level 1)
- Owner Initiates: The owner of a minted digital asset (Level 0) uses a dApp to start the attestation process.
- Schema Handling (SDK): The SDK ensures the correct schema exists by calling
sdk.schemaRegistry.getOrRegisterSchema(). This returns a schemaUID. This step is crucial for defining the structure of the attestation data.
- Bond Token Approval (SDK): The SDK prompts the owner’s wallet to approve the
CASContract to spend the required bondAmount (determined by the chosen Grade) of the designated bond token. This calls sdk.bondToken.approve(). Approval must be granted before the attest call.
- Attestation Call (SDK): The SDK calls
sdk.attestationService.attest(), providing:
attestationRequest: Contains the schemaUID, recipient (usually the owner), expiration time (0 for non-expiring), revocability flag, and any specific attestation data encoded according to the schema.
digitalAssetAddress: The contract address of the asset being attested.
digitalAssetId: The token ID of the asset.
grade: The numerical grade (0-3) chosen by the owner, influencing bond requirements and Level 2 thresholds.
bondAmount: The amount of bond tokens required for the chosen grade’s fee.
- Contract Execution (CAS):
- The
CASContract verifies the inputs (schema exists, grade is valid, etc.).
- It transfers the
bondAmount from the owner to itself using the prior approval.
- It deploys a new
RightsBoundAccount contract specifically for this asset’s attestation, linking the asset to its future royalty and rights management.
- It stores the attestation details (UID, schema, recipient, attester, times, data, asset link, RBA link).
- It updates the asset’s level to 1 (
digitalAssetLevels mapping).
- It emits an
Attested event (containing the attestationUID) and a CulturaRightsBoundAccountDeployed event (containing the RBA address).
- Result (SDK/Dapp): The SDK parses the events from the transaction receipt using
getAttestationDataFromReceipt to extract the attestationUID and the rightsBoundAccount address, returning them to the dApp. The asset is now Level 1 (Attested).
Part 2: Verification (Level 1 -> Level 2)
- Verifier Initiates: A community member or whitelisted verifier uses a dApp to verify/endorse an existing Level 1 attestation, identified by its
attestationUID.
- Bond Token Approval (SDK): The SDK prompts the verifier’s wallet to approve the
CASContract to spend the verificationBond amount they wish to contribute. This calls sdk.bondToken.approve().
- Verification Call (SDK): The SDK calls
sdk.attestationService.verifyAttestation(), providing the attestationUID and the verificationBond amount.
- Contract Execution (CAS):
- The
CASContract verifies the attestation exists and is currently Level 1.
- It transfers the
verificationBond from the verifier to itself.
- It records the verification details, linking the verifier and their bonded amount to the attestation (
verifierCount, communityUserCount, WLVerifierCount, bondedAmount on the Attestation struct are updated).
- It checks if the promotion thresholds (total bonded tokens OR number/type of unique verifiers) defined by the asset’s Grade have now been met by calling the internal
_checkVerifiedVerifiedLevel function.
- If thresholds are met: The contract updates the asset’s level to 2 (
digitalAssetLevels mapping) via _promoteDigitalAsset.
- It emits a
Verified event.
- Result (SDK/Dapp): The SDK returns the transaction result. If the thresholds were met during this verification, the asset is now Level 2 (Verified Rights) and is considered licensable through integrated protocols.
SDK Snippets
import { CulturaSDK } from "@cultura/sdk";
import { parseEther } from "viem"; // Example utility
/**
* Attests an asset, moving it from Level 0 to Level 1.
* Assumes the SDK is initialized for the ASSET OWNER.
*/
async function attestAsset(
sdk: CulturaSDK,
recipientAddress: `0x${string}`, // Usually the owner's address
assetAddress: `0x${string}`,
assetId: bigint,
grade: bigint, // e.g., 3n for Grade C
bondAmount: bigint // e.g., parseEther("0.0001") for Grade C bond fee
) {
try {
// --- 1. Ensure Schema Exists ---
// Define the schema string used for your attestations
const schemaStr =
"string digitalAssetName, string digitalAssetDescription, uint256 grade"; // Example
const isRevocable = true; // Or false, depending on your use case
const schemaUID = await sdk.schemaRegistry.getOrRegisterSchema(
schemaStr,
isRevocable
);
console.log("Using Schema UID:", schemaUID);
// --- 2. Approve Bond Token Spending ---
const casAddress = sdk.config.contracts?.attestationService;
if (!casAddress) throw new Error("CAS address not configured in SDK");
console.log(
`Approving ${bondAmount.toString()} bond tokens for CAS (${casAddress})...`
);
// Ensure the owner has sufficient bond tokens before approving
const approveTxHash = await sdk.bondToken.approve(casAddress, bondAmount);
console.log("Bond Token Approval Tx:", approveTxHash);
// It's crucial to wait for this transaction to be mined before proceeding
await sdk.publicClient.waitForTransactionReceipt({ hash: approveTxHash });
console.log("Approval confirmed.");
// --- 3. Prepare Attestation Request ---
// Encode data according to your schema if necessary. For the example schema,
// you might encode the asset name, description, and grade again here,
// or leave data as '0x' if the core info is implicitly linked via assetId.
const encodedData = "0x" as `0x${string}`; // Placeholder
const attestationRequest = {
schema: schemaUID,
data: {
recipient: recipientAddress,
expirationTime: 0n, // 0 for non-expiring attestations
revocable: isRevocable,
refUID:
"0x0000000000000000000000000000000000000000000000000000000000000000" as `0x${string}`, // Use if referencing another attestation
data: encodedData,
},
};
// --- 4. Call Attest ---
console.log("Submitting attestation to CAS...");
// The attest method waits for the transaction and returns the UID directly
const attestationUid = await sdk.attestationService.attest(
attestationRequest,
assetAddress,
assetId,
grade,
bondAmount // This is the fee/initial bond required by the grade
);
console.log(`Attestation Successful! UID: ${attestationUid}`);
console.log(`Asset is now Level 1 (Attested)`);
// Note: The RightsBoundAccount is deployed automatically during attest.
// You can query for it later using the attestation UID or asset details if needed.
// For simplicity, we're not retrieving it explicitly here.
return { attestationUid }; // Return only the UID for this example
} catch (error) {
console.error("Error during attestation:", error);
throw error; // Re-throw for higher-level handling
}
}
/**
* Verifies an existing Level 1 attestation, potentially promoting it to Level 2.
* Assumes the SDK is initialized for the VERIFIER.
*/
async function verifyAssetAttestation(
sdk: CulturaSDK, // SDK instance initialized for the VERIFIER
attestationUID: `0x${string}`,
verificationBond: bigint // Amount the verifier wants to bond, e.g., parseEther("0.01")
) {
try {
// --- 1. Approve Bond Token Spending ---
const casAddress = sdk.config.contracts?.attestationService;
if (!casAddress) throw new Error("CAS address not configured in SDK");
console.log(
`Approving ${verificationBond.toString()} tokens for CAS (${casAddress})...`
);
// Ensure the verifier has sufficient bond tokens
const approveTxHash = await sdk.bondToken.approve(
casAddress,
verificationBond
);
console.log("Approval Tx:", approveTxHash);
await sdk.publicClient.waitForTransactionReceipt({ hash: approveTxHash });
console.log("Approval confirmed.");
// --- 2. Call Verify Attestation ---
console.log(`Verifying attestation ${attestationUID}...`);
const verifyTxHash = await sdk.attestationService.verifyAttestation(
attestationUID,
verificationBond
);
console.log("Verify Tx:", verifyTxHash);
const receipt = await sdk.publicClient.waitForTransactionReceipt({
hash: verifyTxHash,
});
if (receipt.status !== "success") {
throw new Error("Verification transaction failed");
}
console.log(`Verification submitted successfully.`);
// --- 3. Check the new level (Optional - requires querying state) ---
// This requires knowing the assetAddress and assetId associated with the UID.
// You'd typically fetch this using the attestationUID via the SDK query client or getAttestation.
// Example:
// const attestationData = await sdk.attestationService.getAttestation(attestationUID);
// if (attestationData) {
// const newLevel = await sdk.attestationService.getDigitalAssetLevel(
// attestationData.digitalAssetAddress,
// attestationData.digitalAssetId
// );
// console.log(`Asset level is now: ${newLevel}`);
// if (newLevel === 2) {
// console.log("Asset promoted to Level 2 (Verified Rights / Licensable)!");
// }
// }
return verifyTxHash;
} catch (error) {
console.error("Error during verification:", error);
throw error; // Re-throw for higher-level handling
}
}