Skip to content

TraceLog.sol

Records the seven-stage custody journey of every BatchToken from DELIVERED to SETTLED. Each stage update emits an event read by the API layer, which writes to Hedera HCS for an immutable audit trail.

enum Stage {
DELIVERED, // 0 — BatchToken minted, coffee weighed at collection
GRADED, // 1 — Quality assessment passed
MILLED, // 2 — Processing complete
WAREHOUSED, // 3 — Physical storage confirmed
COMMITTED, // 4 — PurchaseOrder confirmed by buyer
EXPORTED, // 5 — Export licence confirmed — triggers auto-repayment
SETTLED // 6 — Buyer USDC paid, loan repaid, net disbursed
}

Stages must advance in order. No skipping. No reversal.

FromToWho can triggerNotes
DELIVEREDAGENT_ROLEAuto-initializes on first updateStage() call
DELIVEREDGRADEDCOOP_ROLE or AGENT_ROLE (if independent farmer)Quality assessment
GRADEDMILLEDCOOP_ROLE or AGENT_ROLE (if independent)Processing
MILLEDWAREHOUSEDCOOP_ROLE or AGENT_ROLE (if independent)Storage
WAREHOUSEDCOMMITTEDPURCHASE_ORDER_ROLE (PurchaseOrder.sol)On PO confirmation
COMMITTEDEXPORTEDCOOP_ROLE or AGENT_ROLE (if independent)Export permit
EXPORTEDSETTLEDVAULT_ROLE (LendingVault)On buyer USDC receipt

Independent farmers (cooperativeWallet == INDEPENDENT_AGGREGATOR) have no cooperative manager. To prevent their batches from getting stuck:

  • AGENT_ROLE can advance stages GRADED, MILLED, WAREHOUSED, and EXPORTED for independent farmer batches
  • Cooperative farmer batches still require COOP_ROLE for these stages
  • The check is done inside _checkStageRole() by reading the farmer wallet from BatchToken and checking FarmerRegistry.isIndependent()
// Update stage (role-gated, enforces sequential order)
function updateStage(uint256 tokenId, Stage newStage) external;
// Get current stage
function getCurrentStage(uint256 tokenId) external view returns (Stage);
// Initialization
function initialize(address defaultAdmin, address batchTokenAddress) public initializer;
function initializeV2(address farmerRegistryAddress) external reinitializer(2); // Sprint 1
RolePurpose
AGENT_ROLERecord DELIVERED; all stages for independent farmers
COOP_ROLERecord GRADED through EXPORTED for cooperative farmers
PURCHASE_ORDER_ROLERecord COMMITTED (PurchaseOrder.sol)
VAULT_ROLERecord SETTLED (LendingVault.sol)
DEFAULT_ADMIN_ROLEUpgrade contract

The first updateStage() call for any token must pass Stage.DELIVERED. The contract tracks initialization via a private _initialized mapping to distinguish uninitialised tokens (Stage(0)) from tokens actually at DELIVERED (also Stage(0) in the enum).

  • BatchToken.sol — reads getFarmerWallet() to determine batch ownership
  • FarmerRegistry.sol (Sprint 1) — reads isIndependent() for role gating