> ## 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.

# Licensing & Minting Licensed Creative Assets

This guide details the complete process for licensing an existing, verified digital asset (**Level 2: Verified Rights**) and then minting a new **Licensed Creative Asset** based on that license. It primarily uses the **Mock Licensing Protocol** provided by the SDK for testing purposes on Cultura testnets, demonstrating the interactions involved.

## Goal

To create a new digital asset (Licensed Creative Asset) that is verifiably derived from one or more parent assets (Licensable Digital Assets) according to specific license terms, embedding provenance and royalty information on-chain.

## Prerequisites

* A **Licensable Digital Asset** exists (Level 2 Verified Rights). Let's call this the "Parent Asset". Its `collectionAddress` and `tokenId` are known.
* The user wanting to create the derivative work (the "Licensee") has an account with funds (ETH for gas, Bond Tokens for attestation/verification).
* The SDK is initialized for the Licensee (`CulturaSDK.createWithWallet` or `CulturaSDK.createWithAccount`).

## Conceptual Steps

1. **Obtain License:** Secure the rights to use the Parent Asset(s) via a Licensing Protocol.
2. **Mint Derivative:** Create the new Licensed Creative Asset, linking it to the Parent Asset(s) and embedding royalty splits.
3. **Attest Derivative:** Create an attestation for the new Licensed Creative Asset to establish its own rights record (Level 1).
4. **Verify Derivative (Optional):** Verifiers can endorse the new asset's attestation, potentially promoting it to Level 2.

## Flow Using Mock Licensing Protocol (Testnets)

The SDK provides a `MockLicensingProtocolClient` to simulate Step 1 and streamline Steps 2 & 3 for testing.

### Flow Diagram

```mermaid theme={null}
sequenceDiagram
    participant Licensee
    participant Dapp
    participant SDK_Licensee as SDK (Licensee)
    participant MockLicensingProtocol
    participant DigitalAssetContract
    participant CASContract
    participant IPFS
    participant Verifier (Optional)
    participant SDK_Verifier as SDK (Verifier - Optional)

    Licensee->>Dapp: Select Parent Asset (Level 2), Define Derivative Details
    Dapp->>SDK_Licensee: mockLicensingProtocol.licenseDigitalAsset(parentCollection, parentTokenId)
    activate SDK_Licensee
    SDK_Licensee->>MockLicensingProtocol: fakeLicenseIp(parentCollection, parentTokenId)
    MockLicensingProtocol-->>SDK_Licensee: License Granted (Simulated)
    deactivate SDK_Licensee
    Note over Dapp, SDK_Licensee: Step 1: Simulate obtaining license rights

    Licensee->>Dapp: Confirm Derivative Minting Details (Name, Desc, Royalty Split, Grade, Bond)
    Dapp->>SDK_Licensee: attestationService.signForDelegatedAttestation(LicenseeAddress)
    activate SDK_Licensee
    SDK_Licensee->>CASContract: getNonce(LicenseeAddress)
    CASContract-->>SDK_Licensee: Nonce
    %% SDK prepares EIP-712 data for delegatedAttest
    SDK_Licensee-->>Dapp: TypedData for Signing
    deactivate SDK_Licensee
    Note over Dapp, Licensee: Step 2: Prepare for Delegated Attestation

    Dapp->>Licensee: Request Signature (MetaMask popup)
    Licensee->>Dapp: Provides Signature (sigForDelegatedAttest)
    Dapp->>SDK_Licensee: Generate SignatureParams from signature

    Dapp->>SDK_Licensee: mockLicensingProtocol.createLicensedAsset(details, sigParams, schemaUID, bond, grade, parentInfo)
    activate SDK_Licensee
    %% Inside createLicensedAsset (Mock Protocol Client)
    SDK_Licensee->>IPFS: Upload Metadata for New Asset {name, desc, image?}
    activate IPFS
    IPFS-->>SDK_Licensee: metadataURI
    deactivate IPFS
    SDK_Licensee->>DigitalAssetContract: safeMint(Licensee, name, desc, [parentInfo], metadataURI)
    activate DigitalAssetContract
    DigitalAssetContract-->>SDK_Licensee: New Token ID (via Event)
    deactivate DigitalAssetContract
    Note over SDK_Licensee, DigitalAssetContract: Step 3a: Mint the new Licensed Creative Asset

    SDK_Licensee->>CASContract: delegatedAttest(sigParams, attestationReq, newAssetAddress, newTokenId, grade, bond)
    activate CASContract
    CASContract->>CASContract: Verify Signature, Deploy RBA, etc.
    CASContract-->>SDK_Licensee: Attestation UID, RBA Address (via Event)
    deactivate CASContract
    Note over SDK_Licensee, CASContract: Step 3b: Attest the new asset using Licensee's signature
    SDK_Licensee-->>Dapp: Result { licensedAssetId, attestationInfo }
    deactivate SDK_Licensee
    Dapp-->>Licensee: Licensed Creative Asset Minted & Attested (Level 1)

    %% Optional Verification Step
    Verifier->>Dapp: Initiate Verification of New Asset
    Dapp->>SDK_Verifier: mockLicensingProtocol.verifyLicensedAsset(attestationUID, verificationBond)
    activate SDK_Verifier
    SDK_Verifier->>MockLicensingProtocol: verifyLicensedAsset(attestationUID, verificationBond)
    activate MockLicensingProtocol
    MockLicensingProtocol->>CASContract: verifyAttestation(attestationUID, verificationBond)
    activate CASContract
    CASContract-->>MockLicensingProtocol: Verification Result
    deactivate CASContract
    MockLicensingProtocol-->>SDK_Verifier: Verification Success
    deactivate MockLicensingProtocol
    SDK_Verifier-->>Dapp: Verification Result
    deactivate SDK_Verifier
    Note over Dapp, Verifier: Step 4 (Optional): Verify the new Licensed Creative Asset

    Dapp-->>Verifier: Verification Complete
    Dapp-->>Licensee: (Optional) New Asset Verified (Potentially Level 2)

```

### Explanation

1. **Simulate License Acquisition (Mock Protocol):**

   * The Licensee selects a Parent Asset (must be Level 2) via the dApp.
   * The dApp calls `sdk.mockLicensingProtocol.licenseDigitalAsset()`.
   * The mock contract records that this parent asset is "licensed" for testing. (A real protocol would handle actual terms, payments, etc.).

2. **Prepare for Delegated Attestation:**

   * The new Licensed Creative Asset needs attestation by its creator (Licensee). Since the Mock Protocol's convenience function (`createLicensedAsset`) handles minting and attestation together, it requires the Licensee's signature beforehand.
   * The dApp calls `sdk.attestationService.signForDelegatedAttestation()` to get the necessary EIP-712 typed data.
   * The Licensee signs this data via their wallet.
   * The dApp formats the signature using `sdk.attestationService.generateSignatureParams()`.

3. **Create Licensed Asset (Mock Protocol Convenience Function):**

   * The dApp calls `sdk.mockLicensingProtocol.createLicensedAsset()`. **This function bundles several actions:**
     * **Input:** Takes new asset details, the `SignatureParams` from Step 2, a `schemaUID`, `bondAmount`, `grade` for the *new* attestation, and `parentRights` info (parent collection/ID, owner, `royaltySplit`).
     * **Metadata:** Uploads metadata for the new asset to IPFS.
     * **Minting:** Calls `sdk.culturaDigitalAsset.mintDigitalAsset()` to create the new Licensed Creative Asset NFT, passing the `parentRights` array to link it to the parent(s) and embed royalty info.
     * **Delegated Attestation:** Calls `sdk.attestationService.delegatedAttest()` using the Licensee's signature to create the attestation (Level 1) for the new asset and deploy its RBA.
   * Returns the `licensedAssetId` and `attestationInfo` (`attestationUid`, `rightsBoundAccount`).

4. **Optional Verification (Mock Protocol):**

   * To simulate the licensing protocol verifying the new asset's compliance, a Verifier calls `sdk.mockLicensingProtocol.verifyLicensedAsset()`.
   * This takes the `attestationUID` of the *newly created* asset and a `verificationBond`.
   * Internally, the mock protocol calls `sdk.attestationService.verifyAttestation()` on the CAS contract, acting as a verifier for the new asset's attestation. This could promote the new asset to Level 2.

5. **Result:** The Licensed Creative Asset is minted and attested (Level 1), ready for royalty processing or further verification.

### Direct Minting (Without Mock Protocol)

If using a *real* licensing protocol or handling licensing off-chain:

1. Secure the license for the Level 2 Parent Asset(s).
2. Gather the required `parentAssets` data (collection, ID, owner, royaltySplit) based on the license terms.
3. Prepare metadata objects for the new derivative asset (`termsObject`, `tokenUriObject`).
4. Call `sdk.culturaDigitalAsset.mintDigitalAsset()` directly, providing the recipient, new asset details, the populated `parentAssets` array, and the `termsObject` and `tokenUriObject` (or pre-uploaded `termsURI` and `tokenURI` strings). The SDK handles IPFS uploads if objects are provided.
5. Optionally, call `sdk.attestationService.attest()` to attest the newly minted asset (Level 0 -> Level 1).
6. Optionally, have verifiers call `sdk.attestationService.verifyAttestation()` to promote the new asset (Level 1 -> Level 2).

## Registration Patterns

How the metadata (`termsURI`) for the Licensed Creative Asset is structured depends on the use case:

1. **Direct Representation:** The Licensed Creative Asset NFT *is* the product (e.g., a derivative artwork). Its metadata points directly to the artwork file and describes the artwork itself. The link to the parent(s) is embedded via the `parentDigitalAssets` array stored by the `CulturaDigitalAsset` contract during minting.
2. **Reference Representation:** The Licensed Creative Asset NFT acts as a *license token* or voucher. Its metadata describes the *license terms* and might link to an external product (which could be off-chain, a different NFT collection, or require further action). The `parentDigitalAssets` array still links to the original Licensable Digital Asset.

The choice depends on whether the on-chain token directly embodies the licensed output or merely represents the right to access/use it elsewhere.

## SDK Snippets

```typescript theme={null}
import { CulturaSDK, ParentDigitalAsset } from "@cultura/sdk";
import { parseEther, getAddress } from "viem"; // Example utility

/**
 * Simulates licensing a parent asset using the Mock Protocol.
 * Assumes sdk is initialized for the LICENSEE.
 */
async function simulateLicenseAcquisition(
  sdk: CulturaSDK,
  parentCollection: `0x${string}`,
  parentTokenId: bigint
) {
  try {
    console.log(
      `Simulating license acquisition for ${parentCollection}#${parentTokenId}...`
    );
    // Ensure the mock protocol client is available
    if (!sdk.mockLicensingProtocol) {
      throw new Error(
        "Mock Licensing Protocol client not available in SDK config."
      );
    }
    const txHash = await sdk.mockLicensingProtocol.licenseDigitalAsset(
      parentCollection,
      parentTokenId
    );
    await sdk.publicClient.waitForTransactionReceipt({ hash: txHash });
    console.log("Mock license acquired, Tx:", txHash);
  } catch (error) {
    console.error("Error simulating license acquisition:", error);
    throw error;
  }
}

/**
 * Creates and attests a Licensed Creative Asset using the Mock Protocol's
 * convenience function.
 * Assumes sdk is initialized for the LICENSEE.
 */
async function createAndAttestLicensedAsset(
  sdk: CulturaSDK,
  licenseeAddress: `0x${string}`,
  parentRights: readonly ParentDigitalAsset[], // Info about the licensed parent(s) including royaltySplit
  newAssetName: string,
  newAssetDescription: string,
  newAssetImageUrl?: string
) {
  try {
    // Ensure necessary SDK clients are available
    if (
      !sdk.attestationService ||
      !sdk.schemaRegistry ||
      !sdk.bondToken ||
      !sdk.mockLicensingProtocol ||
      !sdk.culturaDigitalAsset
    ) {
      throw new Error(
        "Required SDK clients (Attestation, Schema, BondToken, MockLicensing, DigitalAsset) not configured."
      );
    }

    // --- Prepare for Delegated Attestation ---
    console.log("Preparing signature for delegated attestation...");
    const signatureParams =
      await sdk.attestationService.signForDelegatedAttestation(licenseeAddress);
    console.log("Signature obtained.");

    // --- Define Attestation Parameters ---
    const schemaStr =
      "string digitalAssetName, string digitalAssetDescription, uint256 grade"; // Example schema
    const schemaUID = await sdk.schemaRegistry.getOrRegisterSchema(
      schemaStr,
      true // Assuming revocable
    );
    const grade = 3n; // Example: Grade C for the new attestation
    const bondAmount = parseEther("0.0001"); // Example bond for Grade C attestation fee

    // --- Approve Bond Tokens (Licensee needs to approve CAS for the attestation bond) ---
    const casAddress = sdk.config.contracts?.attestationService;
    if (!casAddress) throw new Error("CAS address not configured");
    console.log(`Approving ${bondAmount} tokens for CAS...`);
    const approveTx = await sdk.bondToken.approve(casAddress, bondAmount);
    await sdk.publicClient.waitForTransactionReceipt({ hash: approveTx });
    console.log("Bond token approved for attestation.");

    // --- Call Mock Protocol's createLicensedAsset ---
    console.log("Calling createLicensedAsset on Mock Protocol...");
    // This function bundles minting and attestation
    const result = await sdk.mockLicensingProtocol.createLicensedAsset(
      {
        name: newAssetName,
        description: newAssetDescription,
        imageUrl: newAssetImageUrl,
      },
      signatureParams,
      schemaUID,
      bondAmount, // Pass the bond amount required for the attestation
      grade,
      parentRights
      // externalNFT parameter can be added here if licensing an external asset
    );

    console.log("createLicensedAsset call successful.");
    console.log(`  New Licensed Asset ID: ${result.licensedAssetId}`);
    console.log(`  Attestation UID: ${result.attestationInfo.attestationUid}`);
    console.log(
      `  Rights Bound Account: ${result.attestationInfo.rightsBoundAccount}`
    );
    console.log(`  Transaction State: ${result.transactionState}`); // Should be 'complete'

    if (result.transactionState !== "complete") {
      throw new Error("createLicensedAsset did not complete successfully.");
    }

    return result;
  } catch (error) {
    console.error("Error creating licensed asset:", error);
    throw error;
  }
}

/**
 * Optional: Simulate verification of the newly created Licensed Creative Asset's attestation.
 * Assumes sdk is initialized for a VERIFIER.
 */
async function simulateVerificationOfLicensedAsset(
  sdk: CulturaSDK, // SDK instance for the VERIFIER
  attestationUID: `0x${string}`,
  verificationBond: bigint // e.g., parseEther("0.01")
) {
  try {
    // Ensure necessary SDK clients are available
    if (
      !sdk.bondToken ||
      !sdk.mockLicensingProtocol ||
      !sdk.attestationService
    ) {
      throw new Error(
        "Required SDK clients (BondToken, MockLicensing, Attestation) not configured."
      );
    }
    // --- Approve Bond Tokens (Verifier needs to approve CAS) ---
    // The mock protocol's verifyLicensedAsset calls CAS.verifyAttestation internally.
    const casAddress = sdk.config.contracts?.attestationService;
    if (!casAddress) throw new Error("CAS address not configured");

    console.log(
      `Approving ${verificationBond} tokens for verification via CAS...`
    );
    const approveTx = await sdk.bondToken.approve(casAddress, verificationBond);
    await sdk.publicClient.waitForTransactionReceipt({ hash: approveTx });
    console.log("Bond token approved for verification.");

    // --- Call Mock Protocol's verifyLicensedAsset ---
    console.log(
      `Calling verifyLicensedAsset for attestation ${attestationUID}...`
    );
    // This simulates the licensing protocol verifying the attestation
    const verifyTxHash = await sdk.mockLicensingProtocol.verifyLicensedAsset(
      attestationUID,
      verificationBond
    );
    await sdk.publicClient.waitForTransactionReceipt({ hash: verifyTxHash });
    console.log("Verification simulation complete, Tx:", verifyTxHash);

    // You could potentially query the asset's level again here to see if it reached Level 2
    // const attestationData = await sdk.attestationService.getAttestation(attestationUID);
    // const level = await sdk.attestationService.getDigitalAssetLevel(attestationData.digitalAssetAddress, attestationData.digitalAssetId);
    // console.log("Asset level after mock verification:", level);
  } catch (error) {
    console.error("Error verifying licensed asset:", error);
    throw error;
  }
}

// --- Example Usage Flow ---
// async function runFullLicensingFlow() {
//   // Assume licenseeSdk is initialized and connected
//   const licenseeSdk = CulturaSDK.createWithWallet(window.ethereum, { chain: 'testnet' });
//   if (!licenseeSdk.walletClient?.account) {
//     console.error("Licensee wallet not connected");
//     return;
//   }
//   const licenseeAddress = licenseeSdk.walletClient.account.address;

//   // Define Parent Asset Info
//   const parentCollection = getAddress('0xParentContractAddress...'); // Use getAddress for checksum
//   const parentTokenId = 1n;
//   const parentOwner = getAddress('0xParentOwnerAddress...'); // Use getAddress

//   try {
//     // 1. Verify Parent Asset is Level 2 (Example Check)
//     const parentLevel = await licenseeSdk.attestationService.getDigitalAssetLevel(parentCollection, parentTokenId);
//     if (parentLevel !== 2) {
//       console.error(`Parent asset ${parentTokenId} is not Level 2.`);
//       return;
//     }
//     console.log("Parent asset confirmed Level 2.");

//     // 2. Simulate getting the license via Mock Protocol
//     await simulateLicenseAcquisition(licenseeSdk, parentCollection, parentTokenId);

//     // 3. Define parent info for the new asset, including royalty split
//     const parentRightsInfo: readonly ParentDigitalAsset[] = [{
//       parentCollection: parentCollection,
//       parentDigitalAssetId: parentTokenId,
//       owner: parentOwner,
//       royaltySplit: 5000n // 50% royalty split example (5000 basis points)
//     }];

//     // 4. Create and attest the new licensed asset using the mock protocol's convenience function
//     const { licensedAssetId, attestationInfo } = await createAndAttestLicensedAsset(
//       licenseeSdk,
//       licenseeAddress,
//       parentRightsInfo,
//       "My Licensed Derivative",
//       "Based on Parent Asset #1",
//       "ipfs://..." // Optional image URL for the new asset
//     );
//     console.log(`New asset ${licensedAssetId} created and attested (Level 1). Attestation: ${attestationInfo.attestationUid}`);

//     // 5. Optional: Have a verifier verify the new asset's attestation using the mock protocol
//     // Assume verifierSdk is initialized for a different account
//     // const verifierSdk = CulturaSDK.createWithWallet(...);
//     // await simulateVerificationOfLicensedAsset(verifierSdk, attestationInfo.attestationUid, parseEther("0.02"));
//     // console.log(`Attestation ${attestationInfo.attestationUid} submitted for verification.`);

//   } catch (e) {
//     console.error("Full licensing flow failed:", e);
//   }
// }

// runFullLicensingFlow();
```
