Skip to main content
A farmer deposits cocoa or maize into a certified warehouse instead of selling it cheap at harvest, gets a CommodityReceiptToken representing the stored quantity, and cashes part of its value out to mobile money the same day — instead of waiting for a buyer. This tutorial builds that flow: deposit → receipt token → fiat payout, plus the expiry tracking a warehouse operator needs as receipts approach their storage limit.

What you’ll build

  • A deposit endpoint that mints a CommodityReceiptToken for a batch as soon as the warehouse operator confirms intake
  • An off-ramp endpoint that lets the farmer convert receipt value to mobile money or bank payout via RampManager
  • A scheduled job that marks receipts EXPIRED once their expiryDate passes

Prerequisites

  • Node.js 18+
  • A Stellar testnet account and secret key (S...), funded via the Stellar Friendbot
  • npm install @ankarachain/sdk express node-cron

1

Scaffold the project

src/config.ts
RampManager here is constructed with factory.adapter and a settlementAddress because this tutorial’s off-ramp step needs the on-chain custody half (depositOffRamp) as well as the off-chain quote/session half. Deploy the settlement contract once per platform with factory.deployRampSettlement({ treasury }) — see Fiat Ramp: On-chain settlement.
2

Issue a receipt token on deposit

Run this each time the warehouse operator confirms a new batch has arrived. The same call works on EVM by swapping the TokenFactory config — see Deploy a Token for the EVM variant.
src/routes.ts
3

Let the farmer cash out to fiat

The farmer picks how many kilograms’ worth of receipt to convert. Quote, initiate the off-ramp session, then deposit the tokens into the settlement contract for custody:
src/routes.ts (continued)
Once your provider confirms the payout (via webhook or by polling ramp.getStatus()), release the custodied tokens to the treasury:
src/routes.ts (continued)
4

Sweep expired batches nightly

Commodity receipts carry a hard expiryDate — past it, the warehouse can no longer honor the stored quantity. Mark expired batches so your UI and any downstream lender can see the status change immediately:
src/jobs/expireBatches.ts
isExpired() and markExpired() are only available on tokens deployed with the "commodity" template — calling them on any other AssetRegistry template throws. See AssetRegistry: Commodity Methods.

Going further

  • Batch warehouses: if one warehouse holds many rotating lots, deployCommodityBatchToken() (ERC-1155-style) may fit better than one CommodityReceiptToken per batch — see Asset Templates: Multi-Token Variants.
  • Lending against a receipt instead of an outright off-ramp cash-out: see the Mining Royalty Platform tutorial, which walks through CollateralVault — the same borrow-against-token pattern applies to warehouse receipts.

Next steps

AssetRegistry

Full reference for isExpired, markExpired, and every other registry method.

RampManager

On-chain settlement, depositOffRamp, confirmOffRampSettlement, refundOffRamp.

Asset Templates

The full CommodityMetadata schema and lifecycle.

CLI Commands

ankara deploy-batch, ankara offramp-initiate, and related commands.