Skip to main content

Import

import {
  createCustodyClient,
  initializeFromShyConfig,
  assertCustodyManifest,
  CUSTODY_MANIFEST_CONTRACT_VERSION   // "shycustody-v1"
} from 'shyware/sdk/web/custodyClient.js'

Initialization

// Participant mode (default)
const client = await initializeFromShyConfig(shyconfig)

// Operator mode — enables build methods for mint, lot record, redemption settlement
const operatorClient = await initializeFromShyConfig(shyconfig, { operatorMode: true })
Operator-gated methods throw assertOperatorMode if called without operatorMode: true. shycustody is a standalone pooled-custody embodiment. The manifest must still declare transfer_layer: "shywire" because silo-unit transfers ride the anonymous transfer rail, but the custody embodiment has its own canonical objects and lifecycle: operator registration, SKU admission, consortium policy, intake lots, issuance, redemption, settlement, and demurrage.

Policy and operators

await client.getCurrentPolicy()
// → ConsortiumPolicy { assetId, operators, fungibilityRules, redemptionRouting }

await client.listPolicies()
await client.getPolicy(policyId)

await client.listOperators()
// → WarehouseOperator[]   (identified by operator commitment hash)

await client.getOperator(operatorId)
The consortium policy is stored on-chain as a canonical record. Accepted SKU whitelist and fungibility rules are read from the policy, not the shyconfig.

Asset and supply

await client.getAsset(assetId)
// → Asset { id, unitOfMeasure, maxSupply, demurragePolicy }

await client.getSupply(assetId)
// → { minted, burned, net }   — publicly auditable conservation

await client.getBalance(assetId, accountCommitment)
// → { balance }   — requires protected linkage store access
getSupply is public — it returns aggregate mint count minus burn count per asset, verifiable by any third party without operator cooperation. getBalance returns the specific holder’s balance and requires the caller to hold a valid account commitment.

SKUs and lots

await client.listSkuClasses()
// → SkuClass[]   (accepted grade bands per asset)

await client.getSkuClass(skuClassId)

await client.listLots()
// → IntakeLot[]

await client.getLot(lotId)
// → IntakeLot { id, operatorId, assetId, quantity, evidenceRefs, height }

Redemptions

await client.listRedemptions()
// → RedemptionRequest[]

await client.getRedemption(requestId)

await client.listSettlements()
// → RedemptionSettlement[]

await client.getSettlement(settlementId)

Demurrage

await client.listDemurrage()
// → DemurrageAssessment[]

await client.getDemurrageAssessment(assessmentId)
Demurrage burns tokens from the supply according to the demurrage_policy in the shyconfig (policy_burn). The burn is applied as a TxType 16 transaction. The effect is visible in getSupply without revealing which holder was affected.

Build and submit (operator)

All build methods return { txJson, ...metadata }. Submit with client.submit(txJson) or pass to dispatch.

Asset registration

// Register a new asset class (operator only)
await operatorClient.buildRegisterAsset({
  assetId: 'vault-gold-1',
  unitOfMeasure: 'troy_oz',
  maxSupply: 100000
})
// → TxType 1

Account registration

// Register a participant account commitment (operator only)
await operatorClient.buildRegisterAccount({
  accountCommitment: H('account:wallet:0xabc...')
})
// → TxType 5

Mint (deposit)

// Issue tokens for a physical deposit (operator only)
await operatorClient.buildMintSilo({
  assetId: 'vault-gold-1',
  amount: 10.5,
  depositorCommitment: accountCommitment,
  lotId: 'intake-lot-001'
})
// → TxType 2
// List 1: (assetId, amount, "mint") — no depositor identity
// List 2: depositorCommitment — no asset amount

Transfer

// Transfer silo units (participant — routed through shywire rail)
const { txJson, transferId, nullifier } = await client.buildTransferSilo({
  assetId: 'vault-gold-1',
  amount: 2.0,
  senderCommitment: myCommitment,
  recipientCommitment: recipientCommitment
})
// → TxType 4
// transferId = H(randomNonce)
// nullifier  = H(senderCommit : assetId : nonce)

Redemption request (participant)

const { txJson, requestId } = await client.buildRequestRedemption({
  assetId: 'vault-gold-1',
  amount: 5.0,
  redeemerCommitment: myCommitment,
  preferredWarehouse: 'warehouse-nyc-1'
})
// → TxType 14

Redemption settlement (operator)

await operatorClient.buildSettleRedemption({
  requestId,
  shippingRef: 'SHIP-12345',
  operatorReceiptRef: 'RCPT-67890'
})
// → TxType 15

Intake lot record (operator)

await operatorClient.buildRecordIntakeLot({
  assetId: 'vault-gold-1',
  operatorId: operatorCommitment,
  quantity: 10.5,
  skuClassId: 'XAU-9999',
  evidenceRefs: {
    camera_session_ref: 'session-abc',
    operator_receipt_ref: 'rcpt-def',
    kyc_attestation_ref: 'kyc-xyz'
  }
})
// → TxType 13

Evidence requirements

The custody shyconfig declares which evidence types are required for an intake lot to be accepted:
"evidence_requirements": ["camera_session_ref", "operator_receipt_ref", "kyc_attestation_ref"]
buildRecordIntakeLot validates that all declared evidence types are provided before building the transaction.