arrow-left

Only this pageAll pages
gitbookPowered by GitBook
1 of 14

On-Chain

Loading...

Kaiko Data

Loading...

Loading...

Loading...

Loading...

Loading...

Third-Party Data

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Kaiko Reference Rates

Introduction

hashtag
Welcome to the Kaiko Data Services documentation.

Kaiko Data Services is an ecosystem that acts as a bidirectional intermediary between various data providers and blockchains. Think of it as a data marketplace that facilitates data feeds designed for regulatory and compliance use cases to be leveraged across various blockchains.

hashtag
Kaiko Data Services comprises three core solutions:

  • Data On-Ramp: Enables off-chain providers to distribute various data products on-chain, powering use cases like NAV calculations mark-to-market accounting for crypto or FX collateral.

  • Data Off-Ramp: Enables network participants and dApp builders to connect their on-chain activity with off-chain facilities & systems, supporting various use cases like reporting, business intelligence, and auditing by removing technical barriers to data access.

  • Data Lifecycle Apps: Enables any business to digitize complex, multi-party workflows that require data sharing and coordination by leveraging a blockchain network as their backend infrastructure.

Learn more about Kaiko Data Services .

herearrow-up-right

Canton request-response oracle

Real-time Reference Rates & Indices on Canton

circle-info

This guide provides step-by-step instructions for creating market data requests to Kaiko using the Kaiko oracle on the Canton blockchain. You can see our full range of Reference Rates and Multi-Asset Indices available on Canton herearrow-up-right.

copy

DAR files must be uploaded on your node to use this Oracle [Click here to download]arrow-up-right

hashtag
Overview

The Kaiko oracle enables secure, paid/licensed requests for real-time Reference Rates and Multi-Asset Indices through the Canton blockchain. The system follows the standard request-response oracle workflow:

  1. Prepare request parameters: Format the Kaiko-specific payload.

  2. Retrieve contracts: Get the current Amulet rules and open mining round contracts.

  3. Exercise a contract choice: Submit a transaction to create a data request.

The oracle processes the request off-chain (settling payment via Amulets) and posts the Kaiko market data response back on-ledger.

hashtag
Prerequisites

hashtag
Required information

You need the following information before starting:

  • Canton participant endpoint: URL of your Canton participant JSON API

  • Canton authentication token: Valid token for API access

  • Consumer party ID: Your party identifier in Canton (e.g., Consumer::1220...)

hashtag
Environment setup

Ensure you have:

  • A Canton node connected to the same domain as the oracle node

  • The Prepaid Oracle DAR uploaded to your Canton node

  • Sufficient Amulet balance for the request

hashtag
Creating a Data Request

hashtag
Prepare provider request parameters

The Kaiko oracle accepts requests for one or more tickers (real-time reference rates and indices).

hashtag
Input model (Kaiko)

You must prepare the payload as valid JSON and then stringify it for the Canton command.

You must first select the Asset Class for the ticker you need to request. One asset class is allowed per request.

Asset Classes:

  • crypto: Array of Kaiko tickers for Crypto Assets. E.g.:

    • KK_RFR_BTCUSD

    • KK_RFR_CCUSD

Example Request 1: Accessing Crypto Data

Example Request 2: Accessing Equity Data

hashtag
Stringified payload

When sending the request to Canton, pass the stringified version (quotes escaped):

hashtag
Submit the request creation transaction

Make a POST request to Canton to exercise the CreateRequest choice.

hashtag
Request Endpoint:

POST

{PARTICIPANT_NODE_JSON_API_URL}/v2/commands/submit-and-wait-for-transaction-tree

Headers

  • Authorization: Bearer {CANTON_TOKEN}

  • Content-Type: application/json

Request body example:

hashtag
Reading the FilledRequest

Once the oracle processes your request, it archives the pending request and creates a FilledRequest contract containing the Kaiko data.

hashtag
Query for FilledRequest

Use the active contracts endpoint to retrieve the result.

hashtag
Request endpoint

POST

{PARTICIPANT_NODE_JSON_API_URL}/v2/state/active-contracts

hashtag
Request body filter: filter for template

{PACKAGE_ID}:PrepaidOracle.Request:FilledRequest.

hashtag
Kaiko response structure

In the resulting FilledRequest contract, the responseData field contains the stringified Kaiko output.

hashtag
Response data model (Kaiko)

The response is a JSON object keyed by rate id(s), containing the returned value(s).

Example (conceptual):

Example FilledRequest content

hashtag
Troubleshooting

hashtag
Invalid JSON structure

  • Cause: The dataRequest string was not properly escaped or contained invalid JSON.

  • Solution: Ensure your stringified JSON does not contain unescaped quotes, stray backslashes, or newlines.

hashtag
Unknown/unsupported rate identifier

  • Cause: One or more requested rate identifiers are not available to your entitlement, are misspelled, or are not supported by the oracle.

  • Solution: Validate the exact rate tickers (e.g., KK_RFR_BTCUSD), confirm access/entitlements, and retry with supported identifiers.

RequestFactory contract ID: Contract ID for the request factory

  • DSO party ID: DSO party identifier

  • Data provider details (provider-dependent): Data request payload (stringified JSON)

  • Amulet contract ID: An Amulet contract to fund payment

  • Payment locked amount: Amount to lock for the request

  • equity: Array of Kaiko tickers for Equity Assets. E.g.:

    • KK_RFR_TSLAUSD

  • JSON
    {
        "crypto": ["KK_RFR_BTCUSD", "KK_RFR_CCUSD"]
    }
    {
        "equity": ["KK_RFR_TSLAUSD"]
    }
    "{\"crypto\":[\"KK_RFR_BTCUSD\",\"KK_RFR_CCUSD\"]}"
    {
        "actAs": [
            "{YOUR_CONSUMER_PARTY}"
        ],
        "commandId": "kaiko-request-1",
        "commands": [
            {
                "actAs": [
                    "{YOUR_CONSUMER_PARTY}"
                ],
                "commandId": "kaiko-request-1",
                "ExerciseCommand": {
                    "contractId": "{REQUEST_FACTORY_CONTRACT_ID}",
                    "templateId": "{PACKAGE_ID}:PrepaidOracle.RequestFactory:RequestFactory",
                    "choice": "CreateRequest",
                    "choiceArgument": {
                        "dso": "{DSO_PARTY}",
                        "amuletRulesCid": "{AMULET_RULES_CONTRACT_ID}",
                        "openRoundCid": "{OPEN_ROUND_CONTRACT_ID}",
                        "inputs": [
                            {
                                "tag": "InputAmulet",
                                "value": "{YOUR_AMULET_CONTRACT_ID}"
                            }
                        ],
                        "dataRequest": "{\"rates\":[\"KK_RFR_BTCUSD\",\"EGLXRT\"]}",
                        "lockedAmount": {PAYMENT_LOCKED_AMOUNT
                        }
                    }
                }
            }
        ],
        "disclosedContracts": [
            {
                "templateId": "{AMULET_PACKAGE_ID}:Splice.AmuletRules:AmuletRules",
                "contractId": "{AMULET_RULES_CONTRACT_ID}",
                "synchronizerId": "{GLOBAL_DOMAIN_SYNCHRONIZER_ID}",
                "createdEventBlob": "{AMULET_RULES_DISCLOSED_BLOB}"
            },
            {
                "templateId": "{AMULET_PACKAGE_ID}:Splice.Round:OpenMiningRound",
                "contractId": "{OPEN_ROUND_CONTRACT_ID}",
                "synchronizerId": "{GLOBAL_DOMAIN_SYNCHRONIZER_ID}",
                "createdEventBlob": "{OPEN_ROUND_DISCLOSED_BLOB}"
            }
        ]
    }
    {
        "KK_RFR_BTCUSD": "{\"value\": 76293.2345454545, \"timestamp\": 1772633545286}",
        "KK_RFR_CCUSD": "{\"value\": 0.1698132000, \"timestamp\": 1772633526495}"
    }
    {
        "oracle": "{ORACLE_PARTY}",
        "consumer": "{YOUR_CONSUMER_PARTY}",
        "requestData": "{\"rates\":[\"KK_RFR_BTCUSD\",\"KK_RFR_BTCUSD\"]}",
        "responseData": "{\"KK_RFR_BTCUSD\": \"{\\\"value\\\": 76293.2345454545, \\\"timestamp\\\": 1772633545286}\",\"KK_RFR_CCUSD\": \"{\\\"value\\\": 0.1698132000, \\\"timestamp\\\": 1772633526495}\"}",
        "serviceAmountCharged": "1.0000000000"
    }

    NCFX

    Data On-Ramp

    Data On-Ramp enables off-chain providers to distribute various data products on-chain, powering use cases like NAV calculations mark-to-market accounting for crypto or FX collateral.

    Data On-Ramp

    Data On-Ramp

    Bloomberg

    Arbitrum

    This guide walks you through consuming Kaiko's Reference Rates on Arbitrum. Several asset pairs are publicly available, meaning you can access free hourly updates or update on-demand by covering the gas fee.

    hashtag
    Read and update manually

    Visit onchain.kaiko.comarrow-up-right. Here, you can see the latest Kaiko price updates for all pairs.

    To update the price:

    1. Click "connect wallet"

    2. Once you've connected your wallet, click "Update"

    3. Authorize the gas payment of 0.00000112 ETH

    4. Wait a few seconds for the prices to be updated

    hashtag
    Update and read on-chain

    To read and update on-chain, you'll need to set up an off-chain listening event using a node provider of your choice to connect to the blockchain.

    hashtag
    To update the price:

    This example script uses Typescript, but you can produce similar results using other coding languages. Running the script requests a price update and listens until it's been updated on-chain. When the update is detected, it alerts your on-chain smart contract.

    1. Install ethers npm install ethers

    2. Send a gas fee with a value of 0.00000112 ETH to the refreshPriceFeeds contract address 0xff2743c44f820c64c94eccfc1b497a1019541097

    1. Wait for at least one block for the price update to be reflected on-chain

    hashtag
    Read the price on-chain:

    This Solidity contract example is triggered by the refreshPriceFeeds function in the Typescript example above. When triggered, it ingests the updated prices on-chain.

    circle-exclamation

    Although one price update refreshes all prices on-chain, you must read each price feed individually. See the price feeds and their corresponding addresses at .

    onchain.kaiko.comarrow-up-right
    import { ethers } from 'ethers';
    
    // Set up your Ethereum provider
    const provider = new ethers.providers.JsonRpcProvider("<YOUR_PROVIDER_URL>");
    
    // Initialize wallet using private key and connect it to the provider
    const wallet = new ethers.Wallet("<YOUR_PRIVATE_KEY>", provider);
    
    // Smart contract address
    const contractAddress = "0xff2743c44f820c64c94eccfc1b497a1019541097";
    
    // ABI definition of the contract's refreshPriceFeeds method
    const abi = [
        "function refreshPriceFeeds() payable external"
    ];
    
    // Create an instance of the contract
    const contract = new ethers.Contract(contractAddress, abi, wallet);
    
    async function refreshPriceFeeds() {
        try {
            // Define the amount of ETH to send (0.00000112 ETH)
            const amountToSend = ethers.utils.parseEther("0.00000112");
    
            // Call the refreshPriceFeeds function, sending 0.00000112 ETH
            const tx = await contract.refreshPriceFeeds({
                value: amountToSend // Send 0.00000112 ETH
            });
    
            // Wait for the transaction to be mined
            console.log("Transaction sent. Waiting for confirmation...");
            const receipt = await tx.wait();
    
            console.log("Transaction confirmed, receipt:", receipt);
        } catch (error) {
            console.error("Error while calling refreshPriceFeeds:", error);
        }
    }
    
    // Call the function
    refreshPriceFeeds();
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    interface IExternalContract {
        function latestUpdate() external view returns (uint256 price, uint256 decimals, uint256 updatedAt);
    }
    
    contract CallLatestUpdate {
        address public externalContractAddress = <PRICE_CONTRACT_ADDRESS>;
    
        // Function to call the latestUpdate() function on the external contract
        function getLatestUpdate() public view returns (uint256 price, uint256 decimals, uint256 updatedAt) {
            // Create an instance of the external contract using the interface
            IExternalContract externalContract = IExternalContract(externalContractAddress);
            
            // Call the latestUpdate() function on the external contract
            return externalContract.latestUpdate();
        }
    }

    Request-response oracle

    circle-info

    This guide provides step-by-step instructions for creating data requests to the NCFX API using the request-response oracle system on the Canton blockchain. The guide assumes that the RequestFactory contract already exists and focuses on the request creation process using HTTP requests.

    copy

    DAR files must be uploaded on your node to use this Oracle [Click here to download]arrow-up-right

    hashtag
    Overview

    The request-response oracle enables secure requests for NCFX financial data through the Canton blockchain. The request creation process involves:

    1. Preparing request parameters: Gather NCFX data request

    2. Retrieving required contracts: Get Amulet rules and open mining round contracts

    3. Contract exercise: Submit the transaction to create a data request on the blockchain

    hashtag
    Prerequisites

    hashtag
    Required Information

    You need the following information before starting:

    • Canton participant endpoint: URL of your Canton participant JSON API

    • Canton authentication token: Valid token for API access

    • Consumer party ID: Your party identifier in Canton (e.g., Consumer::1220...)

    hashtag
    Environment Setup

    Ensure you have:

    • HTTP client capability (curl, fetch, requests library, etc.)

    • JSON parsing capability

    • Access to the Canton blockchain network

    hashtag
    Creating a Data Request

    hashtag
    Prepare NCFX Request Parameters

    Prepare your NCFX data request as a stringified JSON. Example:

    Stringify the request (remove newlines and escape quotes):

    Required Parameters:

    • dataRequest → Stringified NCFX data request

    • lockedAmount → Payment amount to lock

    • dso → The DSO party

    hashtag
    Submit Request Creation Transaction

    Make a POST request to Canton to exercise the CreateRequest choice:

    Request:

    Request body:

    The exerciseResult part in the response looks like:

    hashtag
    Troubleshooting

    A request can fail. In this case, the request contract is archived, and a FailedRequest is created. Here are the failing scenarios:

    hashtag
    1. The locked amount does not cover the service amount

    • Cause: The lockedAmount you specified is less than the service fee charged by the oracle

    • Solution:

      • Increase the lockedAmount parameter in your request

    hashtag
    2. Stringified JSON request Data Is Not Valid

    • Cause: The dataRequest parameter is not a properly stringified JSON string

    • Solution:

      • Verify your JSON is valid before stringifying

    hashtag
    3. NCFX Returns an Error When Sending the Request

    • Cause: NCFX's API returned an error when processing your request

    • Solution:

      • Check NCFX API status and availability

    hashtag
    Reading the FilledRequest

    hashtag
    Request processing

    The oracle

    • detects your new request

    • proceeds to the payment by unlocking your amulet, taking the service amount needed from it and sending back to the consumer party the remaining amount

    • reads the Request contract

    hashtag
    Read the FilledRequest contract

    Once the oracle has processed your request and received a response from NCFX, a FilledRequest contract is created. To retrieve it, you need to query the active contracts on the Canton ledger.

    Query for FilledRequest contracts

    Make a POST request to retrieve your FilledRequest contracts.

    Request:

    Request body:

    Expected response:

    hashtag
    Understanding the response

    The response contains the FilledRequest contract with the following important fields in createArgument:

    Contract fields:

    Service amount: The serviceAmountCharged shows the actual fee deducted from your locked amount. Any remaining amount from your locked amount is returned to you.

    RequestFactory contract ID: Contract ID for the request factory
  • DSO Party ID: DSO party identifier

  • NCFX arguments:

    • Data request (stringified JSON)

  • Amulet contract ID: An Amulet contract for payment

  • Payment locked amount: Amount to lock for the request

  • A canton node connected to the same domain as the oracle's node
  • Uploaded the Request-Response Oracle DAR file to your canton node

  • Sufficient Amulet balance for the request

  • amuletRulesCid → The official amulet rules current contract ID

  • openRoundCid → The current OpenMiningRound contract ID

  • inputs → A list of amulets YOUR_CONSUMER_PARTY owns

  • Ensure you have sufficient Amulet balance

    Ensure all quotes are properly escaped (\")

  • Check that newlines are removed or escaped

  • Test your JSON with a validator before stringifying

  • Verify your data request format matches NCFX's API requirements
  • Ensure requested fields and instruments are available in your catalog

  • Check NCFX rate limits haven't been exceeded

  • Review NCFX's API documentation for valid request formats

  • makes the request to NCFX on your behalf
  • archives the Data Request contract and writes the response data into a FilledRequest which fields look like:

  • oracle

    The oracle party that processed the request

    consumer

    Your consumer party ID

    requestId

    NCFX request ID

    requestData

    The original request data you submitted (stringified JSON)

    responseData

    NCFX's response data (stringified JSON)

    serviceAmountCharged

    The actual service fee charged by the oracle

    {
        "dataset": "live/midrate/v1/rates",
        "currencypair": "GBPUSD",
        "tenor": "SPT",
        "deliverability": "D",
        "type": "O"
    }
    "{\n\"dataset\": \"live/midrate/v1/rates\",\n\"currencypair\": \"GBPUSD\",\n\"tenor\": \"SPT\",\n\"deliverability\": \"D\",\n\"type\": \"O\"\n}"
    POST {PARTICIPANT_NODE_JSON_API_URL}/v2/commands/submit-and-wait-for-transaction-tree
    Authorization: Bearer {CANTON_TOKEN}
    Content-Type: application/json
    
    {
        "actAs": [
            "{YOUR_CONSUMER_PARTY}"
        ],
        "commandId": "create-request-1",
        "commands": [
            {
                "actAs": [
                    "{YOUR_CONSUMER_PARTY}"
                ],
                "commandId": "create-request-1",
                "ExerciseCommand": {
                    "contractId": "{REQUEST_FACTORY_CONTRACT_ID}",
                    "templateId": "3778b498a5b2703be4ab088451b13d3688a81e421c4332cd5e4a0cfa4bd245f1:Request-ResponseOracle.RequestFactory:RequestFactory",
                    "choice": "CreateRequest",
                    "choiceArgument": {
                        "dso": "{DSO_PARTY}",
                        "amuletRulesCid": "{AMULET_RULES_CONTRACT_ID}",
                        "openRoundCid": "{OPEN_ROUND_CONTRACT_ID}",
                        "inputs": [
                            {
                                "tag": "InputAmulet",
                                "value": "{YOUR_AMULET_CONTRACT_ID}"
                            }
                        ],
                        "dataRequest": "{STRINGIFIED_NCFX_DATA_REQUEST}",
                        "lockedAmount": {PAYMENT_LOCKED_AMOUNT}
                    }
                }
            }
        ],
        "disclosedContracts": [
            {
                "templateId": "{AMULET_PACKAGE_ID}:Splice.AmuletRules:AmuletRules",
                "contractId": "{AMULET_RULES_CONTRACT_ID}",
                "synchronizerId": "{GLOBAL_DOMAIN_SYNCHRONIZER_ID}",
                "createdEventBlob": "{AMULET_RULES_DISCLOSED_BLOB}"
            },
            {
                "templateId": "{AMULET_PACKAGE_ID}:Splice.Round:OpenMiningRound",
                "contractId": "{OPEN_ROUND_CONTRACT_ID}",
                "synchronizerId": "{GLOBAL_DOMAIN_SYNCHRONIZER_ID}",
                "createdEventBlob": "{OPEN_ROUND_DISCLOSED_BLOB}"
            }
        ]
    }
    
    
    {
        "transferResult": {
            "round": {
                "number": "29450"
            },
            "summary": {
                "inputAppRewardAmount": "0.0000000000",
                "inputValidatorRewardAmount": "0.0000000000",
                "inputSvRewardAmount": "0.0000000000",
                "inputAmuletAmount": "267332873.0096101736",
                "balanceChanges": [
                    [
                        "kaiko-devnet-1::12200fd571073e933e107afec72902c1a59f4f0ec3be8ef4ada5231c9d3f6d93de2c",
                        {
                            "changeToInitialAmountAsOfRoundZero": "3.4415328900",
                            "changeToHoldingFeesRate": "0.0001168602"
                        }
                    ]
                ],
                "holdingFees": "0.0000000000",
                "outputFees": [
                    "0.0000000000"
                ],
                "senderChangeFee": "0.0000000000",
                "senderChangeAmount": "267332868.0096101736",
                "amuletPrice": "0.1628090000",
                "inputValidatorFaucetAmount": "0.0000000000",
                "inputUnclaimedActivityRecordAmount": "0.0000000000"
            },
            "createdAmulets": [
                {
                    "tag": "TransferResultLockedAmulet",
                    "value": "00ed9e1b4151e1ef755c6299e13daaaf9a4db647e276d531450458afa8379e5a62ca121220ae6ee4724b56f56318093df7cb1eee5e6d8caa35d3ad37ce0b914b02eb86ddff"
                }
            ],
            "senderChangeAmulet": "00dfd2b1bcc153d42ee241a2ac6c524bcc2c5ae5ee0894b918b3bb302b6fc9a173ca1212204132fe27f6d8f1c0531d0ad0bdf80a36828fcf3598baceef12d4b9696da7ebc8",
            "meta": {
                "values": {
                    "splice.lfdecentralizedtrust.org/sender": "kaiko-devnet-1::12200fd571073e933e107afec72902c1a59f4f0ec3be8ef4ada5231c9d3f6d93de2c",
                    "splice.lfdecentralizedtrust.org/tx-kind": "transfer"
                }
            }
        },
        "requestCid": "00ac8fef95efed0583d531ac51ca1eeb2881d1c31f87b31e3ff321eb5441d619e3ca1212206c3d1b48abb01cb7ce28af486debeb9739ec6e8668fec4b19af6255e610fccbb",
        "oracleResponse": "Request created for: {\n\"dataset\": \"live/midrate/v1/rates\",\n\"currencypair\": \"GBPUSD\",\n\"tenor\": \"SPT\",\n\"deliverability\": \"D\",\n\"type\": \"O\"\n} (escrowed 5.0 Amulet until 2026-02-04T15:05:03.617409Z)",
        "lockedAmount": "5.0000000000",
        "requestId": "babc4f56d892"
    }
    {
        "oracle": "{ORACLE_PARTY}",
        "consumer": "{CONSUMER_PARTY}",
        "requestData": "{stringified json request data}",
        "responseData": "{stringified json response data}",
        "serviceAmountCharged": "1.0000000000"
    }
    POST {PARTICIPANT_NODE_JSON_API_URL}/v2/state/active-contracts
    Authorization: Bearer {CANTON_TOKEN}
    Content-Type: application/json
    {
        "activeAtOffset": {offset},
        "verbose": false,
        "eventFormat": {
            "filtersByParty": {
                "{YOUR_CONSUMER_PARTY}": {
                    "cumulative": [
                        {
                            "identifierFilter": {
                                "TemplateFilter": {
                                    "value": {
                                        "includeCreatedEventBlob": false,
                                        "templateId": "3778b498a5b2703be4ab088451b13d3688a81e421c4332cd5e4a0cfa4bd245f1:Request-ResponseOracle.Request:FilledRequest"
                                    }
                                }
                            }
                        }
                    ]
                }
            },
            "verbose": false
        }
    }
    [
        {
            "workflowId": "",
            "contractEntry": {
                "JsActiveContract": {
                    "createdEvent": {
                        "offset": 627962,
                        "nodeId": 1,
                        "contractId": "{FILLED_REQUEST_CONTRACT_ID}",
                        "templateId": "3778b498a5b2703be4ab088451b13d3688a81e421c4332cd5e4a0cfa4bd245f1:Request-ResponseOracle.Request:FilledRequest",
                        "contractKey": null,
                        "createArgument": {
                            "oracle": "{ORACLE_PARTY}",
                            "consumer": "{YOUR_CONSUMER_PARTY}",
                            "requestId": "babc4f56d892",
                            "requestData": "{stringified json request data}",
                            "responseData": "{stringified json response data}",
                            "serviceAmountCharged": "1.0000000000"
                        },
                        "createdEventBlob": "",
                        "interfaceViews": [],
                        "witnessParties": [
                            "{ORACLE_PARTY}"
                        ],
                        "signatories": [
                            "{ORACLE_PARTY}"
                        ],
                        "observers": [
                            "{YOUR_CONSUMER_PARTY}"
                        ],
                        "createdAt": "2026-01-23T14:56:45.708037Z",
                        "packageName": "Request-Response-oracle",
                        "representativePackageId": "3778b498a5b2703be4ab088451b13d3688a81e421c4332cd5e4a0cfa4bd245f1",
                        "acsDelta": true
                    },
                    "synchronizerId": "global-domain::1220...",
                    "reassignmentCounter": 0
                }
            }
        }
    ]

    Canton pull oracle

    circle-info

    This guide provides step-by-step instructions for publishing BTC-USD price data to the Canton blockchain using the pull oracle system. The guide assumes that the MasterOracle and FeedEntitlement contracts already exist and focuses on the publication process using generic HTTP requests.

    copy

    DAR files must be uploaded on your node to use this Oracle [Click here to download]arrow-up-right

    hashtag
    Overview

    The pull oracle system enables secure publication of real-time cryptocurrency market data from Kaiko API to the Canton blockchain. The publication process involves:

    1. Fetching market data: Retrieve signed price data from Kaiko API

    2. Contract exercise: Submit transaction to publish data on blockchain

    The pull oracle workflow has two phases: publish and verify. In the publish phase, the client fetches a Kaiko-signed BTC‑USD observation (value, timestamp, signature) from the Kaiko Market API and submits it to Canton by exercising PublishSignedData on the on-chain MasterOracle contract. The ledger then validates the signature against the oracle key and enforces the caller’s FeedEntitlement; only if both checks pass is a new PublishedSignedData state created on-chain. In the verify phase, any consumer can read the on-chain PublishedSignedData (e.g., via ExtractDataFields) to retrieve the stored fields and rely on the fact that they were accepted only after on-chain verification.

    hashtag
    Prerequisites

    hashtag
    Required information

    You need the following information before starting:

    • Canton participant endpoint: URL of your Canton participant

    • Canton authentication token: Valid JWT token for API access

    • Kaiko API key: Valid API key for accessing Kaiko market data

    hashtag
    Environment setup

    Set up your environment configuration by copying scripts/.env.sample to scripts/.env and filling the required values

    Ensure you have:

    • HTTP client capability (curl, fetch, requests library, etc.)

    • JSON parsing capability

    • Hex encoding/decoding capability

    hashtag
    Upload the dar file to your ledger

    hashtag
    Publishing BTC-USD price data

    hashtag
    Step 1: Fetch market data from Kaiko API

    Make a GET request to retrieve the latest BTC-USD price data with cryptographic signature.

    The price source can be chosen between:

    • Kaiko Fair Market Value price ()

    • Benchmark Reference Rate ().

    Request:

    Expected Response:

    Extract Required Fields:

    • data.name → feedId (e.g., "btc-usd")

    • data.value → price value (e.g., 67850123456)

    • data.decimal → decimal places (e.g., 6)

    hashtag
    Step 2: Submit Publication Transaction

    Make a POST request to Canton to exercise the PublishSignedData choice:

    Request:

    Request body:

    hashtag
    Troubleshooting

    hashtag
    Common error responses

    hashtag
    Signature validation failed

    • Error: "Signature validation failed"

    • Cause: Invalid signature or hash mismatch

    • Solution:

    hashtag
    Feed not authorized

    • Error: "Feed not authorized"

    • Cause: FeedEntitlement doesn't include "btc-usd" feed

    • Solution: Verify your entitlement contract authorizes the btc-usd feed

    hashtag
    Contract not visible

    • Error: "Contract not visible"

    • Cause: Actor party cannot see the contracts

    • Solution:

    hashtag
    Debugging steps

    hashtag
    Verify API response:

    hashtag
    Test canton connection

    hashtag
    Check contract status

    hashtag
    Advanced examples

    hashtag
    Complete implementation example (Python)

    hashtag
    Reference data

    hashtag
    Required contract IDs

    Replace these with your actual contract IDs:

    Contract Type
    Example ID
    Description

    hashtag
    API Endpoints

    Service
    Endpoint
    Purpose

    hashtag
    PublishedSignedData on-chain reading

    Here is a template example for a contract that would read the values of an on-chain PublishedSignedData instance by using the PublishedData interface of the Kaiko data standard that PublishedSignedData implements. You can find this example project in resources/example.

    hashtag
    Exercising ExtractDataFields choice

    To extract the core data fields (feedId, value, decimal, timestamp) from a published data contract, use the following HTTP request:

    Request:

    Request body:

    Expected response

    hashtag
    Response fields

    The extracted values show that the BTC-USD price was 108,542.709812 (108542709812 with 6 decimal places) at timestamp 1761148541270.

    Actor Party ID: Your party identifier in Canton (e.g., PO1-DataProvider::1220...)
  • FeedEntitlement Contract ID: Contract ID for your data feed entitlement

  • data.timestamp → Unix timestamp in milliseconds

  • data.signature → Cryptographic signature from Kaiko

  • canton_oracle.* → Disclosed contract information

  • Ensure signature is DER-encoded secp256k1

  • Verify hash calculation matches exactly

  • Check that signature was generated with correct private key

  • Verify party IDs are correct

  • Ensure contracts exist and are active

  • Check party has proper access permissions

  • MasterOracle (BTC-USD)

    00b59a8e38e273...

    Validates BTC-USD signatures

    FeedEntitlement

    00f6b5eff282ce...

    Authorizes your party for BTC-USD

    Kaiko API

    https://us.market-api.kaiko.io/v2/data/canton/oracle/price/btc-usd

    Fetch signed price data

    Canton API

    PARTICIPANT_JSON_API_URL/v2/commands/submit-and-wait

    Submit transactions

    Canton Health

    PARTICIPANT_JSON_API_URL/v2/health

    Check API status

    _1

    feedId (string)

    The feed identifier, e.g., "btc-usd"

    _2

    value (string)

    The price value as a string representation, e.g., "108542709812"

    _3

    decimal (string)

    Number of decimal places, e.g., "6"

    _4

    timestamp (string)

    Unix timestamp in milliseconds, e.g., "1761148541270"

    https://us.market-api.kaiko.io/v2/data/canton/oracle/price/btc-usdarrow-up-right
    https://us.market-api.kaiko.io/v2/data/canton/oracle/brr/btc-usdarrow-up-right
    cd scripts
    
    # upload
    ./upload_dar.sh
    
    GET https://us.market-api.kaiko.io/v2/data/canton/oracle/price/btc-usd
    X-Api-Key: your-kaiko-api-key
    Content-Type: application/json
    
    {
      "data": {
        "name": "btc-usd",
        "value": 67850123456,
        "decimal": 6,
        "timestamp": 1729677600000,
        "signature": "{signature}"
      },
      "canton_oracle": {
        "template_id": "{PACKAGE_ID}:PullOracle.MasterOracle:MasterOracle",
        "contract_id": "{contract_id}",
        "synchronizer_id": "{sync_id}",
        "created_event_blob": "{base64_encoded_blob}"
      }
    }
    
    POST PARTICIPANT_JSON_API_URL/v2/commands/submit-and-wait
    Authorization: Bearer your-canton-token
    Content-Type: application/json
    
    {
      "actAs": [
        "{your_party_id}"
      ],
      "commandId": "{random_command_id}",
      "commands": [
        {
          "actAs": [
            "{your_party_id}"
          ],
          "commandId": "{random_command_id}",
          "ExerciseCommand": {
            "contractId": "{contract_id}",
            "templateId": "{PACKAGE_ID}:PullOracle.MasterOracle:MasterOracle", 
            "choice": "PublishSignedData",
            "choiceArgument": {
              "actor": "{your_party_id}",
              "value": 67850123456,
              "timestamp": 1729677600000,
              "signature": "{signature}",
              "entitlement": "{your_entitlement_contract_id}"
            }
          }
        }
      ],
      "disclosedContracts": [
        {
          "templateId": "{PACKAGE_ID}:PullOracle.MasterOracle:MasterOracle",
          "contractId": "{contract_id}",
          "synchronizerId": "{sync_id}",
          "createdEventBlob": "{base64_encoded_blob}"
        }
      ]
    }
    
    GET https://us.market-api.kaiko.io/v2/data/canton/oracle/price/btc-usd
    X-Api-Key: your-api-key
    GET PARTICIPANT_JSON_API_URL/v2/health
    Authorization: Bearer your-token
    POST PARTICIPANT_JSON_API_URL/v2/contracts/search
    Authorization: Bearer your-token
    Content-Type: application/json
    
    {
      "templateIds": ["PullOracle.MasterOracle:MasterOracle"]
    }
    
    import requests
    from datetime import datetime
    
    def publish_btc_price():
        # Configuration
        KAIKO_API_KEY = "your-kaiko-api-key"
        KAIKO_URL = "https://us.market-api.kaiko.io/v2/data/canton/oracle/price/btc-usd"
        CANTON_ENDPOINT = "PARTICIPANT_JSON_API_URL"
        CANTON_TOKEN = "your-canton-token"
        ACTOR_PARTY = "{your_party_id}"
        ENTITLEMENT_ID = "00f6b5eff282ced2d6b136058b15302192b912df2fdab80a98b04b49d0ab7e2fb4ca111220af7eec3e2d2a72b6255dd082adaea2ca2f56cdd5c4f15bd0b652874bce2085ce"
    
        # Step 1: Fetch market data
        headers = {
            "X-Api-Key": KAIKO_API_KEY,
            "Content-Type": "application/json"
        }
        
        response = requests.get(KAIKO_URL, headers=headers)
        kaiko_data = response.json()
        
        # Extract data
        value = kaiko_data["data"]["value"]
        timestamp = kaiko_data["data"]["timestamp"]
        signature = kaiko_data["data"]["signature"]
        
        # Step 2: Submit transaction
        command_id = f"publish-btc-{int(datetime.now().timestamp())}"
        
        canton_payload = {
            "actAs": [ACTOR_PARTY],
            "commandId": command_id,
            "disclosedContracts": [
                {
                    "templateId": kaiko_data["canton_oracle"]["template_id"],
                    "contractId": kaiko_data["canton_oracle"]["contract_id"],
                    "synchronizerId": kaiko_data["canton_oracle"]["synchronizer_id"],
                    "createdEventBlob": kaiko_data["canton_oracle"]["created_event_blob"]
                }
            ],
            "commands": [
                {
                    "actAs": [ACTOR_PARTY],
                    "commandId": command_id,
                    "ExerciseCommand": {
                        "contractId": kaiko_data["canton_oracle"]["contract_id"],
                        "templateId": kaiko_data["canton_oracle"]["template_id"],
                        "choice": "PublishSignedData",
                        "choiceArgument": {
                            "actor": ACTOR_PARTY,
                            "value": value,
                            "timestamp": timestamp,
                            "signature": signature,
                            "entitlement": ENTITLEMENT_ID
                        }
                    }
                }
            ]
        }
        
        canton_headers = {
            "Authorization": f"Bearer {CANTON_TOKEN}",
            "Content-Type": "application/json"
        }
        
        canton_response = requests.post(
            f"{CANTON_ENDPOINT}/v2/commands/submit-and-wait",
            json=canton_payload,
            headers=canton_headers
        )
        
        if canton_response.status_code == 200:
            result = canton_response.json()
            print("✅ Price published successfully!")
            print(f"📊 Value: {value}")
            print(f"⏰ Timestamp: {timestamp}")
            return result
        else:
            print(f"❌ Failed: {canton_response.text}")
            return None
    
    # Execute
    if __name__ == "__main__":
        publish_btc_price()
    module PublishedSignedDataValues where
    
    import DataStandard.DataPointV1 (PublishedData(..))
    import DataStandard.Utils (Values)
    import qualified DataStandard.Utils as Utils
    
    -- | Template for extracting values from PublishedData contracts
    -- This template provides utility functions to retrieve specific fields
    -- from contracts implementing the PublishedData interface using the data standard Values interface
    template PublishedSignedDataValues
      with
        subscriber : Party
      where
        signatory subscriber
    
        -- | Choice to extract specific fields from a PublishedData contract
        -- Returns feedId, value, decimal, and timestamp fields
        nonconsuming choice ExtractDataFields : (Text, Int, Int, Int)
          with
            publishedDataId : ContractId PublishedData
          controller subscriber
          do
            -- Fetch the PublishedData contract
            publishedDataView <- view <$> fetch publishedDataId
            
            -- Extract the required fields using Utils.getField from the values
            feedId <- case Utils.getField "feedId" publishedDataView.values of
              Some fid -> return fid
              None -> error "feedId field not found"
            
            value <- case Utils.getField "value" publishedDataView.values of
              Some v -> return v
              None -> error "value field not found"
            
            decimal <- case Utils.getField "decimal" publishedDataView.values of
              Some d -> return d
              None -> error "decimal field not found"
            
            timestamp <- case Utils.getField "timestamp" publishedDataView.values of
              Some t -> return t
              None -> error "timestamp field not found"
            
            return (feedId, value, decimal, timestamp)
    
        -- | Choice to extract all available fields from a PublishedData contract
        -- Returns the complete Values map for inspection
        nonconsuming choice ExtractAllFields : Values
          with
            publishedDataId : ContractId PublishedData
          controller subscriber
          do
            -- Fetch the PublishedData contract and get its view
            publishedDataView <- view <$> fetch publishedDataId
            
            -- Return the complete values map
            return publishedDataView.values
    
        -- | Choice to extract a specific field by name from a PublishedData contract
    
    POST PARTICIPANT_JSON_API_URL/v2/commands/submit-and-wait-for-transaction-tree
    Authorization: Bearer your-canton-token
    Content-Type: application/json
    {
      "actAs": [
        "{subscriber_party}"
      ],
      "commandId": "command-12345",
      "commands": [
        {
          "actAs": [
            "{subscriber_party}"
          ],
          "commandId": "command-12345",
          "ExerciseCommand": {
            "contractId": "00aaa607e80925b65138e43beff8cdc821f01ce50a624bacf35b248d0d28cadaf6ca11122018d0d4db95635c2a717215bfb508c44a00ce3664b9e071309dfa7c9629ea663a",
            "templateId": "PACKAGE_ID:PublishedSignedDataValues:PublishedSignedDataValues",
            "choice": "ExtractDataFields",
            "choiceArgument": {
              "publishedDataId": "0074f05f05f1afc7916d6a88662d42845ddfe2c12347c6937c27f45191c0620466ca111220b27457a819843992a9f752abb90237c48f1185cf9938069c4316c074a23261b4"
            }
          }
        }
      ]
    }
    
    {
        "transactionTree": {
            "updateId": "1220096c8df701c40f11345c3edef0d99adb7eee50ca32a5f42e29ea34fa73ed5ca9",
            "commandId": "test-read-1235",
            "workflowId": "",
            "effectiveAt": "2025-11-21T10:18:03.758127Z",
            "offset": 88255,
            "eventsById": {
                "0": {
                    "ExercisedTreeEvent": {
                        "value": {
                            "offset": 88255,
                            "nodeId": 0,
                            "contractId": "00aaf5adf31cfb40ad7a58febf9c6fafd1e748e16ea7721f5ef7fe871316e159d7ca1212204b8f72a9fc769ef8b2576a59228ad0b82063d6093ef44adc396de184283c27dd",
                            "templateId": "cf9a9eea904a0e53c05f7fa88a3e6038850fa9e68b20ce9b904e50fa080c698b:PublishedSignedDataValues:PublishedSignedDataValues",
                            "interfaceId": null,
                            "choice": "ExtractDataFields",
                            "choiceArgument": {
                                "publishedDataId": "006084902a822faa45b6dc192745b5a37eda78674ef20cc3a7c359d34f346122a6ca1212205d4e937ca7772064d24ab743ae3a50c0006341dcf859f2018ead0a772f6357fb"
                            },
                            "actingParties": [
                                "PO1-DataProvider::12200fd571073e933e107afec72902c1a59f4f0ec3be8ef4ada5231c9d3f6d93de2c"
                            ],
                            "consuming": false,
                            "witnessParties": [
                                "PO1-DataProvider::12200fd571073e933e107afec72902c1a59f4f0ec3be8ef4ada5231c9d3f6d93de2c"
                            ],
                            "lastDescendantNodeId": 1,
                            "exerciseResult": {
                                "_1": "eth-usd",
                                "_2": "286934859756",
                                "_3": "8",
                                "_4": "1763657023776"
                            },
                            "packageName": "kaiko-pull-oracle-example",
                            "implementedInterfaces": [],
                            "acsDelta": false
                        }
                    }
                }
            },
            "synchronizerId": "global-domain::1220be58c29e65de40bf273be1dc2b266d43a9a002ea5b18955aeef7aac881bb471a",
            "traceContext": {
                "traceparent": "00-7eb2a8b9c4ce0358dfabfdef4d98a5d9-fda2f5120fa40f24-01",
                "tracestate": null
            },
            "recordTime": "2025-11-21T10:18:03.913291Z"
        }
    }
    

    Request-response oracle

    circle-info

    This guide provides step-by-step instructions for creating data requests to the Bloomberg DL Per Security API using the request-response oracle system on the Canton blockchain. The guide assumes that the RequestFactory contract already exists and focuses on the request creation process using HTTP requests.

    copy

    DAR files must be uploaded on your node to use this Oracle [Click here to download]arrow-up-right

    hashtag
    Overview

    The request-response oracle enables secure requests for Bloomberg financial data through the Canton blockchain. The request creation process involves:

    1. Preparing request parameters: Gather Bloomberg data request

    2. Retrieving required contracts: Get Amulet rules and open mining round contracts

    3. Contract exercise: Submit the transaction to create a data request on the blockchain

    hashtag
    Prerequisites

    hashtag
    Required Information

    You need the following information before starting:

    • Canton participant endpoint: URL of your Canton participant JSON API

    • Canton authentication token: Valid token for API access

    • Consumer party ID: Your party identifier in Canton (e.g., Consumer::1220...)

    hashtag
    Environment Setup

    Ensure you have:

    • HTTP client capability (curl, fetch, requests library, etc.)

    • JSON parsing capability

    • Access to the Canton blockchain network

    hashtag
    Creating a Data Request

    hashtag
    Prepare Bloomberg Request Parameters

    Prepare your Bloomberg data request as a stringified JSON. Example:

    Stringify the request (remove newlines and escape quotes):

    Required Parameters:

    • dataRequest → Stringified Bloomberg data request

    • lockedAmount → Payment amount to lock

    • dso → The DSO party

    hashtag
    Submit Request Creation Transaction

    Make a POST request to Canton to exercise the CreateRequest choice:

    Request:

    Request body:

    The exerciseResult part in the response looks like:

    hashtag
    Troubleshooting

    A request can fail. In this case, the request contract is archived, and a FailedRequest is created. Here are the failing scenarios:

    hashtag
    1. The locked amount does not cover the service amount

    • Cause: The lockedAmount you specified is less than the service fee charged by the oracle

    • Solution:

      • Increase the lockedAmount parameter in your request

    hashtag
    2. Stringified JSON request Data Is Not Valid

    • Cause: The dataRequest parameter is not a properly stringified JSON string

    • Solution:

      • Verify your JSON is valid before stringifying

    hashtag
    3. Bloomberg Returns an Error When Sending the Request

    • Cause: Bloomberg's API returned an error when processing your request

    • Solution:

      • Check Bloomberg API status and availability

    hashtag
    Reading the FilledRequest

    hashtag
    Request processing

    The oracle:

    • detects your new request

    • proceeds to the payment by unlocking your amulet, taking the service amount needed from it and sending back to the consumer party the remaining amount

    • reads the Request contract

    hashtag
    Read the FilledRequest contract

    Once the oracle has processed your request and received a response from Bloomberg, a FilledRequest contract is created. To retrieve it, you need to query the active contracts on the Canton ledger.

    Query for FilledRequest contracts

    Make a POST request to retrieve your FilledRequest contracts.

    Request:

    Request body:

    Expected response:

    hashtag
    Understanding the response

    The response contains the FilledRequest contract with the following important fields in createArgument:

    Contract fields:

    Service amount: The serviceAmountCharged shows the actual fee deducted from your locked amount. Any remaining amount from your locked amount is returned to you.

    RequestFactory contract ID: Contract ID for the request factory
  • DSO Party ID: DSO party identifier

  • Bloomberg arguments:

    • Data Request (stringified JSON)

    • Catalog (Account ID)

    • Credentials (client_id, client_secret)

  • Amulet contract ID: An Amulet contract for payment

  • Payment locked amount: Amount to lock for the request

  • A canton node connected to the same domain as the oracle's node
  • Uploaded the Request-Response Oracle DAR file to your canton node

  • Sufficient Amulet balance for the request

  • amuletRulesCid → The official amulet rules current contract ID

  • openRoundCid → The current OpenMiningRound contract ID

  • inputs → A list of amulets YOUR_CONSUMER_PARTY owns

  • Ensure you have sufficient Amulet balance

  • Ensure all quotes are properly escaped (\")

  • Check that newlines are removed or escaped

  • Test your JSON with a validator before stringifying

  • Verify your data request format matches Bloomberg's API requirements
  • Ensure requested fields and instruments are available in your catalog

  • Check Bloomberg rate limits haven't been exceeded

  • Review Bloomberg's API documentation for valid request formats

  • makes the request to Bloomberg on your behalf
  • archives the Data Request contract and writes the response data into a FilledRequest which fields look like:

  • oracle

    The oracle party that processed the request

    consumer

    Your consumer party ID

    requestId

    Bloomberg request ID

    requestData

    The original request data you submitted (stringified JSON)

    responseData

    Bloomberg's response data (stringified JSON)

    serviceAmountCharged

    The actual service fee charged by the oracle

    {
        "@type": "DataRequest",
        "name": "TreasuryRequestIBVAL",
        "description": "Treasury Last IBVAL",
        "universe": {
            "@type": "Universe",
            "contains": [
        {
                "@type": "Identifier",
                "identifierType": "TICKER",
                "identifierValue": "FRTR 3.5 11/25/2035 144A Govt"
            }]
        },
        "fieldList": {
            "@type": "DataFieldList",
            "contains": [{"mnemonic": "PX_LAST"}]
        },
        "pricingSourceOptions": {
            "@type": "DataPricingSourceOptions",
            "prefer": { "mnemonic": "IBVL" },
            "exclusive": true
        },
        "trigger": { "@type": "SubmitTrigger"},
        "formatting": {
            "@type": "MediaType",
            "outputMediaType": "application/json"
        }
    }
    
    "{\"@type\":\"DataRequest\",\"name\":\"TreasuryRequestIBVAL\",\"description\":\"TreasuryLastIBVAL\",\"universe\":{\"@type\":\"Universe\",\"contains\":[{\"@type\": \"Identifier\",\"identifierType\": \"TICKER\",\"identifierValue\": \"FRTR 3.5 11/25/2035 144A Govt\"}]},\"fieldList\":{\"@type\":\"DataFieldList\", \"contains\":[{\"mnemonic\":\"PX_LAST\"}]},\"pricingSourceOptions\":{\"@type\":\"DataPricingSourceOptions\",\"prefer\":{\"mnemonic\":\"IBVL\"},\"exclusive\":true},\"trigger\":{\"@type\":\"SubmitTrigger\"},\"formatting\":{\"@type\":\"MediaType\",\"outputMediaType\": \"application/json\"}}"
    
    POST {PARTICIPANT_NODE_JSON_API_URL}/v2/commands/submit-and-wait-for-transaction-tree
    Authorization: Bearer {CANTON_TOKEN}
    Content-Type: application/json
    {
        "actAs": [
            "{YOUR_CONSUMER_PARTY}"
        ],
        "commandId": "create-request-1",
        "commands": [
            {
                "actAs": [
                    "{YOUR_CONSUMER_PARTY}"
                ],
                "commandId": "create-request-1",
                "ExerciseCommand": {
                    "contractId": "{REQUEST_FACTORY_CONTRACT_ID}",
                    "templateId": "3778b498a5b2703be4ab088451b13d3688a81e421c4332cd5e4a0cfa4bd245f1:Request-ResponseOracle.RequestFactory:RequestFactory",
                    "choice": "CreateRequest",
                    "choiceArgument": {
                        "dso": "{DSO_PARTY}",
                        "amuletRulesCid": "{AMULET_RULES_CONTRACT_ID}",
                        "openRoundCid": "{OPEN_ROUND_CONTRACT_ID}",
                        "inputs": [
                            {
                                "tag": "InputAmulet",
                                "value": "{YOUR_AMULET_CONTRACT_ID}"
                            }
                        ],
                			  "catalog": "{CATALOG_ID}",
                			  "clientId": "{CLIENT_ID}",
                			  "clientSecret": "{CLIENT_SECRET}",
                        "dataRequest": "{STRINGIFIED_BLOOMBERG_DATA_REQUEST}",
                        "lockedAmount": {PAYMENT_LOCKED_AMOUNT}
                    }
                }
            }
        ],
        "disclosedContracts": [
            {
                "templateId": "{AMULET_PACKAGE_ID}:Splice.AmuletRules:AmuletRules",
                "contractId": "{AMULET_RULES_CONTRACT_ID}",
                "synchronizerId": "{GLOBAL_DOMAIN_SYNCHRONIZER_ID}",
                "createdEventBlob": "{AMULET_RULES_DISCLOSED_BLOB}"
            },
            {
                "templateId": "{AMULET_PACKAGE_ID}:Splice.Round:OpenMiningRound",
                "contractId": "{OPEN_ROUND_CONTRACT_ID}",
                "synchronizerId": "{GLOBAL_DOMAIN_SYNCHRONIZER_ID}",
                "createdEventBlob": "{OPEN_ROUND_DISCLOSED_BLOB}"
            }
        ]
    }
    
    
    {
        "transferResult": {
            "round": {
                "number": "29450"
            },
            "summary": {
                "inputAppRewardAmount": "0.0000000000",
                "inputValidatorRewardAmount": "0.0000000000",
                "inputSvRewardAmount": "0.0000000000",
                "inputAmuletAmount": "267332873.0096101736",
                "balanceChanges": [
                    [
                        "kaiko-devnet-1::12200fd571073e933e107afec72902c1a59f4f0ec3be8ef4ada5231c9d3f6d93de2c",
                        {
                            "changeToInitialAmountAsOfRoundZero": "3.4415328900",
                            "changeToHoldingFeesRate": "0.0001168602"
                        }
                    ]
                ],
                "holdingFees": "0.0000000000",
                "outputFees": [
                    "0.0000000000"
                ],
                "senderChangeFee": "0.0000000000",
                "senderChangeAmount": "267332868.0096101736",
                "amuletPrice": "0.1628090000",
                "inputValidatorFaucetAmount": "0.0000000000",
                "inputUnclaimedActivityRecordAmount": "0.0000000000"
            },
            "createdAmulets": [
                {
                    "tag": "TransferResultLockedAmulet",
                    "value": "00ed9e1b4151e1ef755c6299e13daaaf9a4db647e276d531450458afa8379e5a62ca121220ae6ee4724b56f56318093df7cb1eee5e6d8caa35d3ad37ce0b914b02eb86ddff"
                }
            ],
            "senderChangeAmulet": "00dfd2b1bcc153d42ee241a2ac6c524bcc2c5ae5ee0894b918b3bb302b6fc9a173ca1212204132fe27f6d8f1c0531d0ad0bdf80a36828fcf3598baceef12d4b9696da7ebc8",
            "meta": {
                "values": {
                    "splice.lfdecentralizedtrust.org/sender": "kaiko-devnet-1::12200fd571073e933e107afec72902c1a59f4f0ec3be8ef4ada5231c9d3f6d93de2c",
                    "splice.lfdecentralizedtrust.org/tx-kind": "transfer"
                }
            }
        },
        "requestCid": "00ac8fef95efed0583d531ac51ca1eeb2881d1c31f87b31e3ff321eb5441d619e3ca1212206c3d1b48abb01cb7ce28af486debeb9739ec6e8668fec4b19af6255e610fccbb",
        "oracleResponse": "Request created for: ..... (escrowed 5.0 Amulet until 2026-02-04T15:05:03.617409Z)",
        "lockedAmount": "5.0000000000",
        "requestId": "babc4f56d892"
    }
    {
        "oracle": "{ORACLE_PARTY}",
        "consumer": "{CONSUMER_PARTY}",
        "requestData": "{stringified json request data}",
        "responseData": "{stringified json response data}",
        "serviceAmountCharged": "1.0000000000"
    }
    POST {PARTICIPANT_NODE_JSON_API_URL}/v2/state/active-contracts
    Authorization: Bearer {CANTON_TOKEN}
    Content-Type: application/json
    {
        "activeAtOffset": {offset},
        "verbose": false,
        "eventFormat": {
            "filtersByParty": {
                "{YOUR_CONSUMER_PARTY}": {
                    "cumulative": [
                        {
                            "identifierFilter": {
                                "TemplateFilter": {
                                    "value": {
                                        "includeCreatedEventBlob": false,
                                        "templateId": "3778b498a5b2703be4ab088451b13d3688a81e421c4332cd5e4a0cfa4bd245f1:Request-ResponseOracle.Request:FilledRequest"
                                    }
                                }
                            }
                        }
                    ]
                }
            },
            "verbose": false
        }
    }
    [
        {
            "workflowId": "",
            "contractEntry": {
                "JsActiveContract": {
                    "createdEvent": {
                        "offset": 627962,
                        "nodeId": 1,
                        "contractId": "{FILLED_REQUEST_CONTRACT_ID}",
                        "templateId": "3778b498a5b2703be4ab088451b13d3688a81e421c4332cd5e4a0cfa4bd245f1:Request-ResponseOracle.Request:FilledRequest",
                        "contractKey": null,
                        "createArgument": {
                            "oracle": "{ORACLE_PARTY}",
                            "consumer": "{YOUR_CONSUMER_PARTY}",
                            "requestId": "babc4f56d892",
                            "requestData": "{stringified json request data}",
                            "responseData": "{stringified json response data}",
                            "serviceAmountCharged": "1.0000000000"
                        },
                        "createdEventBlob": "",
                        "interfaceViews": [],
                        "witnessParties": [
                            "{ORACLE_PARTY}"
                        ],
                        "signatories": [
                            "{ORACLE_PARTY}"
                        ],
                        "observers": [
                            "{YOUR_CONSUMER_PARTY}"
                        ],
                        "createdAt": "2026-01-23T14:56:45.708037Z",
                        "packageName": "Request-Response-oracle",
                        "representativePackageId": "3778b498a5b2703be4ab088451b13d3688a81e421c4332cd5e4a0cfa4bd245f1",
                        "acsDelta": true
                    },
                    "synchronizerId": "global-domain::1220...",
                    "reassignmentCounter": 0
                }
            }
        }
    ]