Physical assets & inventory
Connect asset identity and rights to issuance, transfers and holdings.
← All implementation patternsIssue equipment-related rights and track their holders
Represent contractual rights associated with delivery robot A-001 as 1,000 units and allocate 10 to a partner. Keep equipment information in the asset register and manage issued units and holdings through a token.
Look up an asset ID to display its token, current holders and balances, and allocation and transfer history in one interface.
Architecture and responsibilities
Asset interface
Display equipment, agreements, recipients and quantities.
Web UIApplication API and register
Map asset IDs to tokens and manage permissions and operation IDs.
Application API / DBIssuance and transfers
Enforce the issuance cap, track balances and execute permitted transfers.
FlexibleTokenHelperHistory synchronization
Index confirmed transactions and link them to operating and inspection records.
Transaction receipts / Events
Example data model
Map application records to the data managed by the chain or APIs. Field names and values below illustrate the application model for this architecture.
| Field and example | Stored in | Purpose and mapping |
|---|---|---|
assetIdROBOT-A-001 | Application DB | Stable equipment ID mapped to the serial number or register entry. |
agreementIdAGR-2027-001 | Application DB | Agreement and version defining the represented right. Store originals and personal data under access control. |
chainId + tokenAddresschainId / 0x… | DB and chain | An address identifies different contracts on different chains. Always store both. |
cap / decimals1000 / 0 | Chain | This example uses whole units. Configure the issuance cap and decimals at initialization. |
holder / balancepartnerWallet / 10 | Chain | The chain is authoritative for balances. DB listings are indexed views. |
operationId / transactionHashALLOC-001 / 0x… | Application DB | Track accepted, submitted and confirmed states to avoid duplicate issuance on retries. |
Processing flow
Register the asset and right
Register A-001 and agreement AGR-2027-001. With one contract per asset, the token balance represents units of the right associated with that equipment.
Application DB: assetId → agreementId → tokenAddressInitialize the token
Prepare FlexibleToken with a cap of 1,000 and zero decimals. Grant the issuer MINTER_ROLE and configure transferability and any required allowlist.
FlexibleTokenHelper.deploy / attachAllocate 10 units
Save the allocation request before minting. Associate the confirmed receipt with the request and read the balance. Use transfer with the holder’s signer when moving existing units.
mint(recipient, amount) → balanceOf(recipient)Update the holder view
Index Transfer events using chainId, transactionHash and logIndex as a unique key. Periodically reconcile the display database with on-chain balances.
Transfer events / balanceOfAdd operating records
Link hours, inspections and faults by assetId. Track rights transfers separately from physical delivery, with physical verification handled by the operating system.
Application DB: operating_records.assetId
Before using the code
- Provide an initialized FlexibleToken address and an issuer signer connected to its chain.
- Use a cap of 1,000 and zero decimals for this example. Pass display quantities as strings in units.
- Check MINTER_ROLE, pause state and any eligibility conditions for the recipient.
npm install --save-exact @hazbase/kit@0.9.0 ethers@6.16.0The TypeScript functions below are called from your application with the appropriate connections, permissions and data. Integrate the UI, persistence and recovery using the architecture above.
Allocate units with the equipment ID
Call allocateAsset with assetId, tokenAddress, recipient, units: "10" and issuer. Use its returned values to persist the result in your application API.
import { FlexibleTokenHelper } from '@hazbase/kit';
import { parseUnits } from 'ethers';
type Signer = Parameters<typeof FlexibleTokenHelper.deploy>[1];
export async function allocateAsset(input: {
assetId: string;
tokenAddress: string;
recipient: string;
units: string;
issuer: Signer;
}) {
if (!input.issuer.provider) throw new Error('A connected signer is required.');
// Requires an initialized token and a signer with MINTER_ROLE.
const token = FlexibleTokenHelper.attach(input.tokenAddress, input.issuer);
const amount = parseUnits(input.units, await token.decimals());
if (amount <= 0n) throw new Error('Units must be positive.');
const receipt = await token.mint(input.recipient, amount);
const balance = await token.balanceOf(input.recipient);
const network = await input.issuer.provider.getNetwork();
// Persist this mapping in your asset register after confirmation.
return {
assetId: input.assetId,
chainId: network.chainId.toString(),
tokenAddress: input.tokenAddress,
holder: input.recipient,
allocatedAtomic: amount.toString(),
balanceAtomic: balance.toString(),
transactionHash: receipt.hash,
blockNumber: receipt.blockNumber,
};
}
Result and application integration
For a recipient starting at zero, balanceAtomic becomes "10". Save transactionHash and blockNumber in the operation history. Each mint creates additional units, so page reloads and retries must not call it unconditionally.
Implementation considerations
Quantity units
Passing 10 directly to a token with 18 decimals does not represent 10 display units. The example reads decimals and converts using parseUnits.
Database failure after a successful transaction
The issuance remains on chain. Use a submission process that stores the operation ID and transaction hash before confirmation, then recover results from events.
Different rights per asset
Units of one token are fungible. Different equipment terms may require separate contracts; individually identified items can use a PrivilegeNFT-based design.
Extending the architecture
Inventory and custody
Replace assetId with a storage lot and represent fungible goods as units. Reconcile warehouse delivery confirmation with token burning.
Working with RWA Ops
RWA Ops covers equipment definition, issuance, allocation and operating records together. Compare it with an API/SDK integration for custom interfaces and existing registers.