Blockchain Solution Development for Logistics
Reconciliation of data between carrier, customs, and consignee takes up to 5 days — that's 120 hours of cargo idle time, each hour costing an average of $500 for a sea container. One erroneous Bill of Lading can delay cargo in port for a week and lead to a $2,000 fine. In the TradeLens project (Maersk + IBM), blockchain reduced document processing from 7 days to 2 hours — a 96% acceleration. TradeLens case study, 2020 Our clients achieve the same results: document workflow accelerates by 40%, error rate drops by 60%, and the cost of processing one document decreases by $3.
We will design and implement a turnkey blockchain solution: from supply chain audit to operator training. Fill out the consultation form — we will contact you within a day and show how it works on your case.
What Specifically Blockchain Solves in Logistics
Three problems that cost real money:
Document Authenticity. Bill of Lading — the key document in maritime logistics. Traditionally paper-based, delivered by courier. Electronic B/L (eBL) has existed for a long time, but centralized platforms (essDOCS, Bolero) require trust in the operator. CargoX implements B/L as NFT (ERC-721) on Ethereum — ownership transferable on-chain without an intermediary.
Transparency of Transaction Terms. Smart contract escrow: payment is released automatically upon delivery confirmation. No bank guarantees or letters of credit needed for small deals.
Tracking and Provenance. For pharmaceuticals, luxury goods, food — verification of origin and chain of custody is critical (temperature from 2°C to 8°C, humidity ≤60%). IoT sensors + blockchain = immutable audit trail.
How NFT Document Workflow Works?
Bill of Lading as NFT
contract ElectronicBillOfLading is ERC721, AccessControl {
bytes32 public constant CARRIER_ROLE = keccak256("CARRIER_ROLE");
bytes32 public constant CUSTOMS_ROLE = keccak256("CUSTOMS_ROLE");
struct ShipmentData {
string shipmentId; // external ID from TMS
address shipper;
address consignee;
string portOfLoading;
string portOfDischarge;
string cargoDescription;
uint256 quantity;
string unit; // TEU, tonnes, pallets
uint256 issuedAt;
ShipmentStatus status;
bytes32 dataHash; // hash of full document in IPFS
}
enum ShipmentStatus {
Issued,
InTransit,
ArrivedAtPort,
CustomsCleared,
Delivered,
Surrendered
}
mapping(uint256 => ShipmentData) public shipments;
mapping(uint256 => string[]) public statusHistory; // log of status changes
uint256 private _tokenIdCounter;
function issueBL(
address consignee,
string calldata shipmentId,
string calldata portOfLoading,
string calldata portOfDischarge,
string calldata cargoDescription,
uint256 quantity,
string calldata unit,
bytes32 dataHash
) external onlyRole(CARRIER_ROLE) returns (uint256) {
uint256 tokenId = ++_tokenIdCounter;
_mint(consignee, tokenId);
shipments[tokenId] = ShipmentData({
shipmentId: shipmentId,
shipper: msg.sender,
consignee: consignee,
portOfLoading: portOfLoading,
portOfDischarge: portOfDischarge,
cargoDescription: cargoDescription,
quantity: quantity,
unit: unit,
issuedAt: block.timestamp,
status: ShipmentStatus.Issued,
dataHash: dataHash
});
emit BLIssued(tokenId, consignee, shipmentId);
return tokenId;
}
function updateStatus(
uint256 tokenId,
ShipmentStatus newStatus,
string calldata note
) external {
ShipmentData storage shipment = shipments[tokenId];
if (newStatus == ShipmentStatus.CustomsCleared) {
require(hasRole(CUSTOMS_ROLE, msg.sender), "Only customs");
} else if (newStatus == ShipmentStatus.Delivered) {
require(ownerOf(tokenId) == msg.sender, "Only consignee");
} else {
require(hasRole(CARRIER_ROLE, msg.sender), "Only carrier");
}
ShipmentStatus prevStatus = shipment.status;
shipment.status = newStatus;
statusHistory[tokenId].push(string(abi.encodePacked(
Strings.toString(block.timestamp), ":", note
)));
emit StatusUpdated(tokenId, prevStatus, newStatus, msg.sender);
}
// Override transfer — B/L can only be transferred under certain statuses
function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize)
internal override
{
super._beforeTokenTransfer(from, to, tokenId, batchSize);
if (from != address(0)) {
ShipmentStatus status = shipments[tokenId].status;
require(
status == ShipmentStatus.Issued || status == ShipmentStatus.InTransit,
"BL not transferable in current status"
);
}
}
}
Escrow for Payments
Payment frozen in smart contract until delivery confirmation:
contract ShipmentEscrow {
enum EscrowState { Created, Funded, Released, Disputed, Refunded }
struct Escrow {
address buyer;
address seller;
address carrier;
uint256 amount;
address token; // USDC or other stablecoin
uint256 blTokenId; // ID of B/L NFT
address blContract;
EscrowState state;
uint256 releaseDeadline; // if no dispute before deadline — auto-release
}
mapping(bytes32 => Escrow) public escrows;
function createEscrow(
address seller,
address carrier,
uint256 amount,
address token,
uint256 blTokenId,
address blContract,
uint256 deliveryDeadline
) external returns (bytes32 escrowId) {
escrowId = keccak256(abi.encodePacked(msg.sender, seller, blTokenId, block.timestamp));
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
escrows[escrowId] = Escrow({
buyer: msg.sender,
seller: seller,
carrier: carrier,
amount: amount,
token: token,
blTokenId: blTokenId,
blContract: blContract,
state: EscrowState.Funded,
releaseDeadline: deliveryDeadline + 7 days
});
}
function confirmDelivery(bytes32 escrowId) external {
Escrow storage escrow = escrows[escrowId];
require(msg.sender == escrow.buyer, "Only buyer");
require(escrow.state == EscrowState.Funded, "Wrong state");
ElectronicBillOfLading bl = ElectronicBillOfLading(escrow.blContract);
require(
bl.shipments(escrow.blTokenId).status == ElectronicBillOfLading.ShipmentStatus.Delivered,
"Not delivered on-chain"
);
escrow.state = EscrowState.Released;
IERC20(escrow.token).safeTransfer(escrow.seller, escrow.amount);
}
}
Blockchain is Faster Than Traditional Databases
Blockchain replaces multiday reconciliation in Excel and email with a single secure ledger. Transactions are confirmed in minutes, not hours. In the TradeLens project, shipping time was reduced from 10 days to 1 day. This uses a shared ledger with PoA (Proof of Authority) consensus on a permissioned network — high throughput (up to 1000 TPS) and low latency.
How to Integrate Blockchain with Existing TMS?
Logistics TMS and ERP (SAP, Oracle) have REST/SOAP APIs. Our integration layer signs events and sends transactions on-chain:
class LogisticsIntegration {
private web3Provider: Provider;
private blContract: ElectronicBillOfLading;
// Webhook from TMS on cargo status change
async handleTMSStatusUpdate(event: TMSEvent) {
const { shipmentId, newStatus, timestamp, operator } = event;
const tokenId = await this.getTokenIdByShipmentId(shipmentId);
const onChainStatus = this.mapTMSStatusToOnChain(newStatus);
// Send transaction
const tx = await this.blContract.updateStatus(
tokenId,
onChainStatus,
`TMS update: ${newStatus} at ${timestamp}`
);
await tx.wait();
// Update local DB
await this.db.shipments.update({
where: { shipmentId },
data: { lastTxHash: tx.hash, onChainStatus },
});
}
}
How IoT Telemetry Gets on Blockchain?
For cold chain (pharma, food), verification of storage conditions is critical. IoT sensors transmit data via a gateway (Raspberry Pi) to a smart contract through an oracle. We use Chainlink Functions for decentralized aggregation:
contract ShipmentTelemetry {
struct TelemetryRecord {
uint256 timestamp;
int16 temperature; // in tenths of degrees (156 = 15.6°C)
uint16 humidity; // in tenths of percent
int32 latitude; // in microdegrees
int32 longitude;
address oracle; // who signed the data
}
mapping(uint256 => TelemetryRecord[]) public telemetry; // tokenId => records
mapping(uint256 => bool) public conditionViolated; // whether violations occurred
// Allowed ranges for cargo
struct ConditionRequirements {
int16 minTemp;
int16 maxTemp;
uint16 maxHumidity;
}
mapping(uint256 => ConditionRequirements) public requirements;
function submitTelemetry(
uint256 shipmentTokenId,
int16 temperature,
uint16 humidity,
int32 lat,
int32 lon,
bytes calldata oracleSignature
) external {
bytes32 dataHash = keccak256(abi.encodePacked(
shipmentTokenId, temperature, humidity, lat, lon, block.timestamp / 300
));
address signer = ECDSA.recover(dataHash.toEthSignedMessageHash(), oracleSignature);
require(isApprovedOracle(signer), "Unauthorized oracle");
telemetry[shipmentTokenId].push(TelemetryRecord({
timestamp: block.timestamp,
temperature: temperature,
humidity: humidity,
latitude: lat,
longitude: lon,
oracle: signer
}));
ConditionRequirements memory req = requirements[shipmentTokenId];
if (temperature < req.minTemp || temperature > req.maxTemp || humidity > req.maxHumidity) {
conditionViolated[shipmentTokenId] = true;
emit ConditionViolation(shipmentTokenId, temperature, humidity, block.timestamp);
}
}
}
Blockchain Selection: Public vs Private vs Hybrid
Choice depends on requirements for confidentiality and composability.
| Criterion | Public (Polygon, Arbitrum) | Private (Hyperledger Fabric) | Hybrid |
|---|---|---|---|
| Access | Permissionless | Permissioned | Permissioned + public hashes |
| Confidentiality | Low (everyone sees) | High | Medium |
| Gas | Yes | No | No on private layer |
| Composable with DeFi | Yes | No | No |
| Transaction Speed | ~100-200 TPS | ~1000+ TPS | Depends on layer |
| Infrastructure Cost | Low (public nodes) | High (own nodes) | Medium |
For B2B consortium with known participants — Hyperledger Fabric. For open protocol with tokenization — Polygon with private transactions.
Consensus Comparison for Logistics
| Consensus | Throughput | Latency | Energy Consumption | Example |
|---|---|---|---|---|
| PoA | ~1000 TPS | ~1 sec | Low | Hyperledger Fabric |
| PoS (Ethereum) | ~15-30 TPS | ~12 sec | Medium | Ethereum mainnet |
| Tendermint | ~1000 TPS | ~2 sec | Low | Cosmos SDK |
| PoW | ~7 TPS | ~10 min | High | Bitcoin (not applicable) |
Common Implementation Mistakes
- Ignoring off-chain data: not all documents need to be stored on-chain; use IPFS + hashes.
- Lack of role-based access: who can issue B/L, change status — embed roles.
- Weak integration with legacy: without a webhook adapter, TMS will remain isolated.
What is Included in the Work (Deliverables)
- Architectural documentation (interaction diagrams, contract specifications)
- Smart contracts with unit tests (Foundry / Hardhat)
- Integration layer (Node.js/Fastify) with API
- Frontend panel (Next.js + wagmi)
- Changelog and audit report (Mythril/Slither)
- Operator training (2–4 hours)
- Technical support for 3 months after release
Timeline and Cost
Approximate timelines:
- MVP (B/L NFT + basic tracking + simple escrow) — from 6 weeks
- Production (IoT, multi-party workflow, full integration) — from 4 months
Cost is calculated individually after audit. Fill out the form — we will contact you within a day. Order a system demonstration on your case — we will show a live prototype.
Our Experience
We are a team of blockchain engineers with 5+ years of experience in Web3. We have delivered over 10 projects for logistics, fintech, and DeFi. We use only verified OpenZeppelin libraries, ERC standards, and undergo formal smart contract audits.
Get a consultation: contact us via email or Telegram — we will show cases and architecture for your task.







