Most clients come to us with the same problem: their supply chain tracking systems are just a database with a web interface. The issue isn't technology but the architecture of trust: data is recorded by one party, and others are forced to believe it. When five parties from three countries are involved in the chain, this model breaks down. This is where blockchain solves a real problem: ensuring record immutability and public verifiability without a central arbitrator. Our team has many years of experience in blockchain development and has delivered over 15 projects in the supply chain space. For example, recording a single tracking event on Polygon costs about $0.005, which is incomparably cheaper than $5–10 on Ethereum mainnet. Polygon beats Ethereum by more than 1000x in transaction cost and by tens of times in finality speed. Gas for recording on a private Hyperledger Fabric network is fractions of a cent, e.g., $0.0001 per transaction.
How Blockchain Solves the Trust Problem in Supply Chain
Choosing a network is the first step. For corporate supply chains with a known set of participants, a permissioned network like Hyperledger Fabric or Besu in IBFT mode is better suited. A public blockchain via L2 (Polygon, Base) is justified when public verifiability is important, e.g., for QR scanning by consumers.
What to Store On-Chain vs. Off-Chain
The main mistake of beginner projects is trying to store everything on-chain. Result: expensive, slow, redundant. Rule: only document hashes, actor identifiers (addresses), timestamps, statuses (enum), and merkle roots of data batches are stored on-chain. Off-chain storage includes photos, PDF certificates, detailed sensor readings, and large JSON objects. A link to the storage (IPFS CID) and the content hash are recorded on-chain. IPFS provides decentralized storage with hash verification.
Why IPFS instead of the cloud?
Cloud storage is tied to a single provider—a single point of failure. IPFS is decentralized: data is duplicated across many nodes, accessible by hash, and integrity is verified cryptographically. For supply chains, this guarantees that no participant can retroactively alter a product's history.// Tracking event: lightweight on-chain, details in IPFS
struct TrackingEvent {
bytes32 batchId; // Batch/lot ID
bytes32 dataHash; // keccak256 of the full JSON event
string ipfsCid; // CID of full data in IPFS
address actor; // who records (verified participant)
EventType eventType; // PRODUCED, SHIPPED, RECEIVED, INSPECTED, SOLD
uint256 timestamp;
bytes32 locationHash; // hash of GPS coordinates (for privacy)
}
enum EventType { PRODUCED, SHIPPED, RECEIVED, INSPECTED, CERTIFIED, SOLD }
mapping(bytes32 => TrackingEvent[]) public batchHistory;
mapping(bytes32 => bool) public authorizedActors;
event BatchEvent(
bytes32 indexed batchId,
EventType indexed eventType,
address indexed actor,
bytes32 dataHash,
string ipfsCid
);
function recordEvent(
bytes32 batchId,
bytes32 dataHash,
string calldata ipfsCid,
EventType eventType
) external {
require(authorizedActors[keccak256(abi.encode(msg.sender, eventType))],
"Not authorized for this event type");
TrackingEvent memory evt = TrackingEvent({
batchId: batchId,
dataHash: dataHash,
ipfsCid: ipfsCid,
actor: msg.sender,
eventType: eventType,
timestamp: block.timestamp,
locationHash: bytes32(0)
});
batchHistory[batchId].push(evt);
emit BatchEvent(batchId, eventType, msg.sender, dataHash, ipfsCid);
}
Managing Participant Access with DID
In a supply chain, there are several types of actors: producer, logistician, customs, retailer, inspector. Simple Ownable is not suitable—a role-based system with delegation is needed. W3C DID Core is a standard for decentralized identity. Each participant has a DID linked to their smart contract addresses. Participant verification (KYB) happens off-chain through accredited verifiers who issue Verifiable Credentials (VC).
// VC verification when registering a participant
import { Resolver } from 'did-resolver'
import { getResolver as ethrResolver } from 'ethr-did-resolver'
import { verifyCredential } from 'did-jwt-vc'
async function verifyParticipantCredential(
vcJwt: string,
participantAddress: string
): Promise<boolean> {
const resolver = new Resolver({
...ethrResolver({ infuraProjectId: process.env.INFURA_ID })
})
const result = await verifyCredential(vcJwt, resolver)
// Check that VC was issued by an accredited verifier
const trustedIssuers = await getTrustedIssuers() // from smart contract
if (!trustedIssuers.includes(result.issuer)) {
return false
}
// Check that VC belongs to this address
return result.verifiableCredential.credentialSubject.ethereumAddress
.toLowerCase() === participantAddress.toLowerCase()
}
Role-Based Access with Time Windows
A participant may have the right to record events only during a specific period (e.g., cargo transit time):
struct ActorPermission {
bytes32 role; // PRODUCER_ROLE, SHIPPER_ROLE, etc.
uint256 validFrom;
uint256 validUntil;
bytes32[] allowedBatches; // empty array = all batches
}
mapping(address => ActorPermission[]) public permissions;
function isAuthorized(
address actor,
bytes32 role,
bytes32 batchId
) public view returns (bool) {
ActorPermission[] storage perms = permissions[actor];
for (uint i = 0; i < perms.length; i++) {
if (perms[i].role == role &&
perms[i].validFrom <= block.timestamp &&
perms[i].validUntil >= block.timestamp) {
if (perms[i].allowedBatches.length == 0) return true;
for (uint j = 0; j < perms[i].allowedBatches.length; j++) {
if (perms[i].allowedBatches[j] == batchId) return true;
}
}
}
return false;
}
How to Integrate IoT with Blockchain?
Sensor data must land on-chain automatically and immutably. This is an architectural problem: an IoT device cannot sign Ethereum transactions directly (no RAM, no battery for EVM-class cryptography). We use a Gateway + Oracle pattern: an edge gateway aggregates sensor data, signs it, publishes to IPFS, and an oracle service sends the transaction to the smart contract.
# Oracle service: verification and recording of sensor event
from web3 import Web3
from eth_account import Account
import ipfshttpclient
async def process_sensor_reading(gateway_id: str, payload: dict, signature: str):
# 1. Verify gateway signature
message = encode_defunct(text=json.dumps(payload, sort_keys=True))
recovered = w3.eth.account.recover_message(message, signature=signature)
gateway_address = await get_registered_gateway(gateway_id)
if recovered.lower() != gateway_address.lower():
raise ValueError("Invalid gateway signature")
# 2. Publish to IPFS
async with ipfshttpclient.connect() as ipfs:
cid = ipfs.add_json(payload)
# 3. Record on-chain
data_hash = Web3.keccak(text=json.dumps(payload, sort_keys=True))
tx = tracking_contract.functions.recordSensorEvent(
payload['batch_id'].encode(),
data_hash,
cid,
EventType.SENSOR_READING
).build_transaction({
'from': oracle_account.address,
'nonce': w3.eth.get_transaction_count(oracle_account.address),
'maxFeePerGas': await get_gas_price(),
})
signed = oracle_account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
return tx_hash.hex()
For high trust requirements, we use an HSM (Hardware Security Module) directly in the device. The Microchip ATECC608 is an inexpensive chip with an ECC key pair that cannot be extracted. The device signs data with a key that is physically protected from compromise.
Example: Pharmaceutical Supply Chain
Consider a pharmaceutical supply chain (FDA DSCSA compliance requires electronic tracking). Main events:
- Event 1: Production — record batch ID, date, composition, CoA hash. A QR code with batchId is generated.
- Event 2: Shipment — the logistician scans the QR, records carrier ID, tracking number, temperature range.
- Event 3: Customs Clearance — record declaration, status, inspector ID.
- Event 4: Receipt — date, physical inspection, discrepancies. Hash verification.
- Event 5: Sale to consumer — the consumer scans the QR and sees the full history.
How We Do It: 5 Steps
- Business process analysis — identify key events, participant roles, and data entry points.
- On/Off-chain model design — determine what to store on blockchain vs. IPFS.
- Smart contract development — implement tracking, role system, and access control.
- IoT and ERP integration — configure gateway, oracle, and API for ERP/WMS.
- Pilot and deployment — test with real data, train participants.
Network Selection
| Parameter | Public L2 (Polygon/Base) | Hyperledger Fabric | Besu (IBFT) |
|---|---|---|---|
| Public verifiability | Yes | No | No |
| Cost per write | ~$0.001–$0.01/tx | Almost 0 | Almost 0 |
| Finality speed | 2–5 sec | < 1 sec | 2–5 sec |
| Access control | Smart contracts | Native channel/MSP | Smart contracts |
| Regulatory requirements | Public blockchain | Private network | Private network |
Development Phases
| Phase | Duration | Result |
|---|---|---|
| Design | 2–3 weeks | Business process analysis, on/off-chain data model |
| Smart contracts | 3–4 weeks | Tracking contracts, role system, tests |
| Oracle + IoT | 3–4 weeks | Gateway integration, oracle service, IPFS pipeline |
| API & Dashboard | 3–4 weeks | REST/GraphQL API, admin panel, consumer verifier |
| Integration & Pilot | 2–4 weeks | ERP/WMS integration, pilot |
The most time-consuming phase is integration with participants' legacy ERP systems, not blockchain development.
What's Included
Smart contracts: event tracking, role system, access control. Backend: API for ERP/WMS integration, oracle service for IoT data processing. Dashboard: admin panel and consumer-facing product verification module. Documentation: architecture diagram, API specification, deployment guide. Training: training for key participants in the chain. Support: warranty service and optional extension.
Contact us to evaluate your project. Order end-to-end supply chain tracking development.







