Physical assets & inventory https://docs.hazbase.com/en/patterns/assets/ IMPLEMENTATION PATTERNS Physical assets & inventory Connect asset identity and rights to issuance, transfers and holdings. ← All implementation patterns EXAMPLE Issue 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 0 1 Asset interface Display equipment, agreements, recipients and quantities. Web UI 0 2 Application API and register Map asset IDs to tokens and manage permissions and operation IDs. Application API / DB 0 3 Issuance and transfers Enforce the issuance cap, track balances and execute permitted transfers. FlexibleTokenHelper 0 4 History 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 assetId ROBOT-A-001 Application DB Stable equipment ID mapped to the serial number or register entry. agreementId AGR-2027-001 Application DB Agreement and version defining the represented right. Store originals and personal data under access control. chainId + tokenAddress chainId / 0x… DB and chain An address identifies different contracts on different chains. Always store both. cap / decimals 1000 / 0 Chain This example uses whole units. Configure the issuance cap and decimals at initialization. holder / balance partnerWallet / 10 Chain The chain is authoritative for balances. DB listings are indexed views. operationId / transactionHash ALLOC-001 / 0x… Application DB Track accepted, submitted and confirmed states to avoid duplicate issuance on retries. Processing flow 1 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 → tokenAddress 2 Initialize 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 / attach 3 Allocate 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) 4 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 / balanceOf 5 Add 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. Install npm install --save-exact @hazbase/kit@0.9.0 ethers@6.16.0 The 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. pattern-assets.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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 , } ; } Download the example 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. Related API and SDK reference FlexibleTokenHelper PrivilegeNFTHelper Factory Start with the sample application Deployment and operations