SupplyChain Example

This example provides the infrastructure necessary to track physical objects in a real-world supply chain on the Sawtooth Ledger. Records are tracked on the Ledger, and are uniquely linked to the physical objects they represent.

SupplyChain Overview


Agents represent actors in the system. An Agent is expected most commonly to represent a company. Agents are identified by their public keys and any transactions an agent submits must be signed by the agent. Agents serve two roles in the system: owners and custodians. Owners are Agents that are the legal holders of the physical object a record represents. Custodians are Agents that have possesion of the physical object that a Record represents. Custodians may be transportation, logistics, warehousing companies, or any other company that has entered into an agreement to care for the item.

The ledger representation of “Agents” is:

// Agents are essentially public keys registered with a human readable name on
// the chain. They will be able to create Records and submit Applications for
// ownership or custodianship.
message Agent {
    string identifier = 1; // the hex-encoded public key of the Agent
    string name = 2; // a human readable name

// Container for on-chain Agents.
// Allows multiple to be saved at a single address in case of hash collision.
message AgentContainer {
    // List of Agents - more than one implies a state address collision
    repeated Agent entries = 1;


Records represent physical objects in the world. Records are identified in the system by natural keys. These natural keys could be the serial number of the object, the RFID tag number, etc.

Records keep a list of all the Owners and Custodians that have possessed the item: these records are stored in chronological order from oldest to most recent. The most recent is the current Agent in that role.

Records may also be finalized. When a record is finalized it means the physical item has been consumed or destroyed and there can be no more updates to the Record.

The ledger representation of “Records” is:

// A Record tracks a physical item with a history of its owners and custodians.
message Record {
    string identifier = 1; // the natural key of the record, serial number or
    // attached sensor identifier

    int64 creation_time = 2; // the time the record was created

    message AgentRecord {
        string agent_identifier = 1; // the public key of the agent
        int64 start_time = 2; // the time the agent started in the role
    repeated AgentRecord owners = 3; // list of the owners, ordered from oldest
    // to newest. The first by definition is the creator of the record.
    // The last is the current owner of the record.
    repeated AgentRecord custodians = 4; // ordered list of custodians.
    // Same ordering as the owners list.

    bool final = 5; // is the record finalized, finalized records cannot be
    // changed.

// Container for on-chain Records.
// Allows multiple to be saved at a single address in case of hash collision.
message RecordContainer {
    // List of Records - more than one implies a state address collision
    repeated Record entries = 1;


Applications are the mechanism that Agents use to change roles (Owner or Custodian). Agents that wish to assume a new role for a Record create a new application that specifies the desired role. The current Agent in that role must then accept the Application before the Record is updated. This allows both Agents to assert their agreement to the change before the update is made. The applicant(proposing Agent) may cancel the Application or the current Agent may reject the Application if it is not wanted.

The ledger representation of “Applications” is:

// Applications are a request for ownership or custodianship of a Record.
message Application {

    string record_identifier = 1; // the natural key of the record
    string applicant = 2; // public key of the applicant
    int64 creation_time = 3;

    // Whether this Application is a request for ownership or custodianship
    enum Type {
        OWNER = 0;
        CUSTODIAN = 1;
    Type type = 4;

    // The current acceptance status
    enum Status {
        OPEN = 0;
        CANCELED = 1;
        REJECTED = 2;
        ACCEPTED = 3;
    Status status = 5;

    string terms = 6; // human readable terms

// Container for on-chain Applications.
// Allows multiple to be saved at a single address in case of hash collision.
message ApplicationContainer {
    // List of Applications - more than one implies a state address collision
    repeated Application entries = 1;

SupplyChain Transaction Family

The SupplyChain Transaction Family is implemented by a Transaction Processor that manages ledger(global state) representation and integrity.

SupplyChain REST API

The SupplyChain REST API implements a domain specific api for clients to query SupplyChain Ledger state.