PDARP Developer Docs

Read Particula Digital Asset Risk Passport (PDARP) ratings directly from your smart contracts.

testnet sandbox · v1.3 · Updated 2026-06-19 · Public · integrators

PDARP is the onchain delivery layer for Particula's digital-asset risk ratings. It lets any protocol read a current rating grade and risk metadata for a tokenized asset with a single view call - no permission, allowlist, token, or fee required. Use it to drive collateral factors, borrow gating, liquidations, listing decisions, or governance signals.

These docs cover the testnet sandbox. For production / mainnet integration, reach out to the Particula team (integrations@particula.io).


1. Concepts

1.1 What you can read

PDARP is the Rating Passport - a single onchain risk credential per asset (the terms are interchangeable: PDARP = Particula Digital Asset Risk Passport). You read the passport's data by the asset's rating key:

  1. Rating score - the headline risk rating on the AAA … D letter scale, as an encoded value plus a timestamp.
  2. Risk dimension sub-scores - the three PDARF dimensions behind the headline (Counterparty, Structural, Underlying Asset), so you can react to a specific dimension rather than only the headline rating.
  3. Report pointer - an IPFS CID linking to the full human-readable rating report and its metadata.
  4. Provenance - the Particula signer and attestation reference attached to each published rating, so you can verify it came from Particula.

The passport is delivered onchain as a non-transferable (soulbound) NFT - the token form of the credential, which anchors the asset and indexes the data above (see §3.4). Reads are plain view calls and are fully permissionless; ratings are published and maintained by Particula, you only ever read.

RATING KEY Ethereum-0x77…2aec PDARP the Rating Passport Rating score getValueString → "AA+" Risk dimension sub-scores Counterparty · Structural · Underlying · Transparency Report IPFS CID → full rating report Provenance signer + attestation, verifiable
PDARP (the Rating Passport) for an asset — one key resolves its rating score, sub-scores, report and provenance, all permissionless view reads.

1.2 How an asset is identified - the rating key

Assets are addressed by a string key, not a contract-address argument:

"<Blockchain>-<TokenAddress>"
e.g.  "Ethereum-0x7712c34205737192402172409a8f7ccef8aa2aec"

You pass this key to the read functions. Some data is also addressable per-dimension using a key suffix (see §3.2). The same key works across the supported networks.

1.3 The rating encoding

A rating is returned as a uint128 code plus a uint128 timestamp. The code is a 4-nibble value 0x[L1][L2][L3][M]:

  • L1..L3 are letter positions: A = 0xA, B = 0xB, C = 0xC, D = 0xD, right-padded with 0x0.
  • M is the modifier: 0 = "-", 1 = (none), 2 = "+".
Grade Code Grade Code
AAA+ 0xAAA2 A+ 0xA002
AAA 0xAAA1 A 0xA001
AA+ 0xAA02 BBB 0xBBB1
AA 0xAA01 CCC+ 0xCC02
AA- 0xAA00 DDD+ 0xDDD2

A convenience read function returns the decoded grade string ("AA+") directly, so most integrators never decode by hand. A reference decoder is in §6.

You may also see a 0-100 / 0-10000 numeric score (higher = better) on some surfaces - notably the interactive demo (§7), which uses a simplified numeric score to keep the collateral-factor math easy to read. It represents the same rating; the canonical onchain encoding is the 4-nibble letter-grade code above. To avoid handling either raw form, call getValueString, which returns the letter grade directly. If you do read a raw numeric score, map it with the band table in §6.

1.4 Freshness

Each rating carries the timestamp it was published. The aggregated read endpoint filters out stale feeds and returns a value only when enough fresh sources agree. Always apply your own maximum-age check appropriate to your risk logic (for example, treat a rating older than your threshold as "no fresh rating" and do not change parameters).


2. Quickstart (EVM)

Read a grade in under five minutes. Offchain sanity check with Foundry's cast:

cast call <PDARP_RATING_READER> \
  "getValueString(string)(string,uint128)" \
  "Ethereum-0x<tokenAddress>" \
  --rpc-url <sepolia_rpc>
# → ("AA+", 1718700000)

From a contract:

interface IPDARPReader {
    function getValue(string calldata key) external view returns (uint128 code, uint128 timestamp);
    function getValueString(string calldata key) external view returns (string memory grade, uint128 timestamp);
}

contract Example {
    IPDARPReader public pdarp;

    function gradeOf(string calldata key) external view returns (string memory) {
        (string memory grade,) = pdarp.getValueString(key);
        return grade; // e.g. "AA+"
    }
}

3. EVM read reference

3.1 Rating score

interface IPDARPReader {
    /// Encoded grade code + timestamp. Reverts if no fresh rating is available.
    function getValue(string calldata key) external view returns (uint128 code, uint128 timestamp);

    /// Diagnostic read that does not apply freshness checks.
    function getValueView(string calldata key) external view returns (uint128 code, uint128 timestamp);

    /// Decoded grade string (e.g. "AA+") + timestamp. Returns "" for an unknown key.
    function getValueString(string calldata key) external view returns (string memory grade, uint128 timestamp);
}
  • The recommended reader aggregates multiple sources and applies staleness filtering. getValue therefore reverts when there is no fresh rating (treat a revert as "no rating right now," not a zero score), and getValueView is the unchecked diagnostic variant.
  • On that aggregated reader, the returned timestamp is the aggregation time, not the moment the rating was published. If you need the exact publish time, read the per-source value.
  • getValueString returns an empty string for an unknown key.

3.2 Risk dimension sub-scores

Beyond the headline rating, you can read the PDARF risk dimensions behind it with a single call that returns the composite plus per-dimension values, in the same grade encoding as the headline (§1.3) - decode with the helper in §6.

interface IPDARPDimensions {
    /// Composite + per-dimension values + timestamp.
    function getTokenRatings(string calldata key) external view returns (
        uint128 composite,
        uint128 counterparty,
        uint128 structural,
        uint128 underlying,
        uint128 transparency,
        uint128 timestamp
    );
}

This lets a protocol react to a specific dimension - for example tightening only when counterparty risk deteriorates, even if the headline rating is unchanged. In Particula's product surfaces you will also see these dimensions shown as 0-100 sub-scores (e.g. Counterparty 93/100); the 0-100 form and the grade encoding describe the same dimension.

Category transition (planned). The call shape and encoding above match the live contract and are stable. The dimension categories are transitioning to four - Counterparty, Structural, Underlying, and Transparency (shown above); until that lands, the live contract returns its existing dimension fields. Only the categories change - the call shape and grade encoding do not. For the headline rating today, use getValueString (§3.1).

3.3 Report pointer (IPFS)

Every rating links to its full report - the human-readable assessment, methodology context, and metadata - stored on IPFS. Read the CID onchain, then fetch the document from any IPFS gateway.

interface IPDARPReport {
    /// IPFS CID for the full rating report, + timestamp.
    function getValue(string calldata key) external view returns (string memory cid, uint256 timestamp);
}
const [cid] = await report.getValue(key)
const url = `https://ipfs.io/ipfs/${cid}`   // or your preferred gateway

3.4 The passport token (onchain form of the PDARP)

The PDARP is delivered onchain as a non-transferable (soulbound) NFT - this token is the Rating Passport. The token does not store the score itself; it anchors the asset and indexes the rating key(s) used to resolve the live data in §3.1-§3.3, and carries the credential metadata and report pointer. Read it when you want a single, stable onchain handle for "this asset is covered by Particula" that you can reference, display, or gate on.

interface IRatingPassport {
    /// Metadata URI for the passport (points to the credential document).
    function tokenURI(uint256 tokenId) external view returns (string memory);
    /// Report CID currently associated with a passport token.
    function cidOf(uint256 tokenId) external view returns (string memory);
}

Typical flow: resolve an asset's passport → read its metadata → use the embedded rating key(s) to read the live grade and pillars.

3.5 Events to index

Track rating changes in your indexer or keeper by subscribing to update events.

/// Emitted whenever a rating value for a key is published or updated.
event OracleUpdate(string key, uint128 code, uint128 timestamp);

Subscribe by reader-contract address and decode in your indexer. A downgrade is simply an OracleUpdate with a lower grade code than the previous value for the same key - watch the sequence per key to detect upgrades, downgrades, and staleness.

3.6 Verifying provenance (the rating signer)

Every published rating is signed by an authorized Particula key. You can verify this fully onchain - useful when a protocol must prove that a risk-parameter change was driven by a genuine Particula rating.

interface IPDARPProvenance {
    /// Rating value + timestamp + the Particula signer that attested it + an attestation reference.
    function getOracleData(string calldata key) external view returns (
        uint256 value,
        uint256 timestamp,
        address signer,
        bytes32 attestationRef
    );

    /// Whether an address is an authorized Particula rating signer.
    function isAuthorizedSigner(address signer) external view returns (bool);
}

Pattern: read the rating with its signer, assert isAuthorizedSigner(signer) is true, and check the timestamp against your freshness window before acting. attestationRef is a stable handle to the specific attestation, for audit trails.

This verifies Particula - not the asset issuer. Ratings are published and signed by Particula independently, including unsolicited ratings, so no action by the rated asset's issuer is required for a rating to exist or be read. The check above confirms a genuine Particula signer, nothing more. Where an asset issuer is separately onboarded, the passport may additionally reference that issuer's onchain identity as an optional attribute you can inspect - but it is never a precondition for a rating.

3.7 Behavior notes

  • All reads are view; no permission, fee, or token is needed.
  • Prefer the aggregated reader (§3.1) for the headline grade - it applies staleness and consensus filtering.
  • A revert on getValue means "no fresh rating," not a zero score.
  • Decode any grade code (composite or pillar) with the helper in §7, or use the *String convenience getters.

4. Solana read reference

On Solana, the rating for a key lives in a program-derived account you read directly.

4.1 Derive the account and decode

import { keccak256, toUtf8Bytes } from 'ethers'
import { PublicKey, Connection } from '@solana/web3.js'

const conn = new Connection('https://api.devnet.solana.com')
const pdarpProgram = new PublicKey('<PDARP_SOLANA_PROGRAM>')   // see §6

// Store PDA (singleton)
const [storePda] = PublicKey.findProgramAddressSync([Buffer.from('Store')], pdarpProgram)

// Per-key rating account: seeds = ["OracleData", store, keccak256(key)]
const key = 'Ethereum-0x7712c34205737192402172409a8f7ccef8aa2aec'
const keyHash = Buffer.from(keccak256(toUtf8Bytes(key)).slice(2), 'hex')
const [dataPda] = PublicKey.findProgramAddressSync(
  [Buffer.from('OracleData'), storePda.toBuffer(), keyHash],
  pdarpProgram,
)

const info = await conn.getAccountInfo(dataPda)
// decode the account (see layout below), then:
const ratingU16 = Number(decoded.price & 0xffffn)   // grade code; decode via §7

4.2 Account layout

The rating account is laid out (little-endian) as:

discriminator(8) | store(32) | keyHash(32) | symbolLen(u32) | symbol(≤64)
  | price(u128) | timestamp(u64) | signer(20) | attestationRef(32) | updatedAt(i64) | bump(1)

The grade code is the low 16 bits of price (price & 0xffff) and decodes with the same scheme as EVM (§1.3, §7). The signer and attestationRef fields give you the same provenance you get on EVM (§3.6). A Rating Passport is also available on Solana as a Metaplex Core NFT that indexes an asset's rating keys.


5. Networks & addresses (testnet sandbox)

Network Chain / cluster What Address
Ethereum Sepolia 11155111 PDARP rating reader (EVM) 0xBed74Bf4845414375922c73c370e27bf929Fbb05 (sandbox - confirm current address with the Particula team)
Solana Devnet devnet PDARP program 2wcyUrY2QCWfZudneTH8cxBRUkczw1prQ7FbyrSJJ3vY
Solana Devnet devnet Rating Passport collection 291ahzabmDXkrzGJ32WacViYT61XxRPoS6jeErGcHCpx

Sandbox RPCs: Sepolia - any public Sepolia endpoint; Solana - https://api.devnet.solana.com.

Sandbox addresses are for evaluation and may rotate. For stable production addresses and mainnet availability, contact integrations@particula.io.


6. Reference decoder

Decode a grade code to its letter string in any environment:

// 4-nibble code → letter grade, e.g. 0xAA02 → "AA+"
function decodeGrade(code: number): string {
  const l1 = (code >> 12) & 0xf, l2 = (code >> 8) & 0xf, l3 = (code >> 4) & 0xf, m = code & 0xf
  const L = (n: number) => (n === 0 ? '' : String.fromCharCode(n + 55)) // 10→'A' … 13→'D'
  const mod = m === 0 ? '-' : m === 2 ? '+' : ''
  return `${L(l1)}${L(l2)}${L(l3)}${mod}`
}

If you instead read a raw numeric score (0-10000, e.g. from the demo), map it to a grade with the band table below. The canonical encoding remains the 4-nibble code above; prefer getValueString when you can.

// numeric score (0–10000, higher = better) → letter grade
function gradeFromScore(score: number): string {
  if (score >= 9300) return 'AAA+'; if (score >= 9000) return 'AAA'; if (score >= 8700) return 'AAA-'
  if (score >= 8400) return 'AA+';  if (score >= 8100) return 'AA';  if (score >= 7800) return 'A+'
  if (score >= 7400) return 'A';    if (score >= 7000) return 'BBB'; if (score >= 6500) return 'BB-'
  if (score >= 5500) return 'B';    if (score >= 4500) return 'C';   return 'D'
}

7. Integration patterns

Try it live → lending-demo.app.particula.io is an interactive lending-protocol sandbox that reads live testnet PDARP ratings. Walk through the patterns below - rating-driven collateral factors, downgrade-triggered liquidation, instability gating, and an RWA NAV-plus-rating scenario - running end to end. (The demo shows the rating as a simplified 0-10000 numeric score; see §1.3 - the canonical onchain encoding is the letter-grade code.)

PDARP supports two broad integration archetypes, and you can mix them:

  • Direct feed - your contract reads the rating inline and derives a parameter (e.g. collateral factor) on the fly. Fully autonomous, tightest coupling.
  • Governance / risk-steward signal - you read the rating (onchain or offchain) and feed it to your governance or risk process, which adjusts parameters. Lowest coupling, keeps a human or DAO in the loop.

7.1 Rating-derived collateral factor

Map the grade to a collateral factor (CF) and cap it per asset:

CF = min(maxCF, rating-implied factor)
function ratingCF(string calldata key) public view returns (uint256) {
    (uint128 code, uint128 ts) = pdarp.getValue(key);
    require(block.timestamp - ts <= maxStaleness, "stale rating");
    uint256 implied = cfFromGrade(code);     // your mapping from grade → bps
    return implied < maxCF ? implied : maxCF; // cap per asset
}

A downgrade lowers the CF, which lowers borrower health factors and can trigger your existing liquidation flow - no special-case code beyond reading the rating.

7.2 Downgrade → liquidation

Because CF is derived from the live grade, a rating downgrade automatically reduces the health factor of positions backed by that asset. Positions that fall below your liquidation threshold become liquidatable through your normal flow. Index OracleUpdate (§3.5) to react in the same block as a downgrade if you want proactive keepers.

7.3 Instability gate (block new borrows)

Use the rating's update history to gate risk. If an asset's grade has changed more than your tolerated number of times within a window, treat it as unstable and block new borrows / collateral while still allowing repayment and liquidation. This protects the pool during turbulent re-ratings without trapping users.

7.4 Multi-asset portfolio health

Read a grade per collateral asset, derive a CF for each, and compute a weighted health factor across the portfolio. Cascading downgrades across correlated assets surface as a single deteriorating health factor - the same shape as a standard weighted-collateral calculation.

7.5 Dimension-targeted policy

Read the risk dimension sub-scores (§3.2) and set policy on a specific dimension. For example: freeze new borrows if the Counterparty sub-score drops below a threshold, or require higher overcollateralization if the Structural sub-score weakens - even when the headline rating is unchanged.

7.6 RWA: rating alongside a price / NAV feed

For tokenized real-world assets, combine PDARP with your price or NAV feed. The NAV tells you what the asset is worth; the PDARP rating tells you how much risk sits behind it. A rating downgrade can move your risk parameters even when NAV is stable - capturing risk a price feed alone would miss.

7.7 Provable, auditable parameter changes

When governance changes a parameter based on a rating, record the signer and attestationRef (§3.6) alongside the change. Anyone can later verify onchain that the change was driven by a genuine, Particula-signed rating at a known time.

In all cases: check the timestamp against your own freshness threshold before acting.


8. On the roadmap

Forward-looking and illustrative of the PDARP concept - not commitments or timelines. Built around one idea: a digital asset's full risk profile, delivered onchain as a composable, continuously-updated credential your contracts can consume. As the framework grows, more of that profile becomes directly fetchable.

8.1 From a rating feed to a multi-party risk credential

Today you read a rating. The direction of travel is a Rating Passport that is a multi-party onchain data object: a single credential per asset whose fields are written by the parties authorized to attest them, each within strict write permissions enforced onchain - for example the rating score by Particula, net asset value by the fund administrator, and proof-of-reserve by the custodian. The result is a composable, cryptographically verifiable credential with a full, auditable contribution history - so a consumer can see not just the rating, but who attested each data point and when.

The passport carries a stable lifecycle state - Active, Suspended, Revoked, or Expired - that a consuming protocol can gate on directly (e.g. freeze new positions when an asset's passport is Suspended), independent of the numeric score.

8.2 Future data points (illustrative)

The passport is organized into expanding data blocks. Representative fields we are working toward exposing onchain:

Block Example data points
Live rating Rating score + tier, risk dimension sub-scores, transparency score, confidence interval, red flags
Pricing Price discovery, liquidity depth, valuation
Proof-of-reserve Verification, third-party attestation, assurance status
Lifecycle Token identifiers across chains, rating history, lifecycle state, controls
Alerts Security, compliance, and performance monitoring signals

A few of these are worth calling out for risk logic:

  • Transparency score - how complete/disclosed the underlying data is, separate from the rating itself.
  • Confidence interval - the uncertainty around a score, so you can widen safety margins when confidence is low.
  • Red flags - discrete per-attribute indicators you can gate on without interpreting the full score.

Plus capabilities to consume them: onchain rating history for trend-aware logic, push / subscribe so contracts and keepers react in the same block as a change, verifiable rating proofs, portfolio / batch queries, and the same keys and read surface across more EVM chains and non-EVM ecosystems.

8.3 Tiered and credentialed access

  • Tiered reads - summary scores at the public tier; full sub-scores, pricing, lifecycle, alert detail, and the data-contributor panel at credentialed and protocol tiers.
  • Credentialed write / autonomous actions - protocols and AI agents issued revocable onchain identities that define which PDARP-triggered operations they may execute (e.g. adjust collateral parameters, pause deposits, update allowlists, initiate liquidations) within hard, contract-enforced boundaries, with every read and write logged for a tamper-proof audit trail.

If a specific data point would unblock your integration, tell us at integrations@particula.io - roadmap priority is shaped by integrator demand.


9. FAQ

Do I need permission or a key to read? No. All reads are permissionless view calls.

Is there a fee? No fee to read.

How often do ratings update? Ratings are maintained continuously and republished as inputs change. Always rely on the returned timestamp plus your own freshness threshold rather than an assumed cadence.

What does a revert mean on getValue? No fresh rating is currently available for that key. Do not treat it as a zero score.

Can I trust that a value came from Particula? Yes - verify it onchain via the rating signer and the authorized-signer registry (§3.6).

Does a rating require the asset issuer's participation? No. Ratings are signed and published by Particula independently, including unsolicited ratings. The asset issuer's identity is an optional attribute, not a precondition (§3.6).

Which assets are covered? Coverage is expanding. Ask the Particula team for the current list of rated assets and their keys.

Can I read on mainnet? Mainnet availability and stable addresses are provided on request - contact integrations@particula.io.


10. Support

  • Integration support and production access: integrations@particula.io
  • Website: particula.io

Particula's risk ratings are independent analytical assessments and do not constitute investment advice, credit ratings, or regulatory opinions. Particula operates as a risk rating provider and is not a credit rating agency as defined under applicable regulatory frameworks, including EU Regulation (EC) No 1060/2009. Particula's assessments do not include forward-looking assumptions and do not assess creditworthiness or default likelihood.

Independent Risk Rating Provider · particula.ioPARTICULA