Розробка блокчейн-рішення для логістики
Реконсиляція даних між перевізником, митницею та отримувачем займає до 5 днів — це 120 годин простою вантажу, кожна година якого коштує в середньому $500 для морського контейнера. Один помилковий Bill of Lading може затримати вантаж у порту на тиждень і призвести до штрафу $2,000. У проекті TradeLens (Maersk + IBM) блокчейн скоротив обробку документів з 7 днів до 2 годин — це 96% прискорення. Наші клієнти досягають таких же результатів: документообіг прискорюється на 40%, частота помилок падає на 60%, а вартість обробки одного документа знижується на $3.
Ми спроектуємо та впровадимо блокчейн-рішення під ключ: від аудиту ланцюга поставок до навчання операторів. Залиште заявку на консультацію — ми зв'яжемося протягом дня та покажемо, як це працює на вашому кейсі.
Що конкретно вирішує блокчейн у логістиці
Три проблеми, які коштують реальних грошей:
Справжність документів. Bill of Lading — ключовий документ у морській логістиці. Традиційно — паперовий, передається кур'єром. Електронний B/L (eBL) давно існує, але централізовані платформи (essDOCS, Bolero) вимагають довіри до оператора. CargoX реалізує B/L як NFT (ERC-721) на Ethereum — ownership трансферабельний on-chain без посередника.
Прозорість умов угоди. Смарт-контракт-ескроу: оплата вивільняється автоматично при підтвердженні доставки. Не потрібні банківські гарантії або акредитиви для невеликих угод.
Трекінг та provenance. Для фармацевтики, люксу, продовольства — критична верифікація origin та ланцюга зберігання (температура від 2°C до 8°C, вологість ≤60%). IoT-сенсори + блокчейн = незмінний audit trail.
Як працює NFT-документообіг?
Bill of Lading як 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; // зовнішній ID з TMS
address shipper;
address consignee;
string portOfLoading;
string portOfDischarge;
string cargoDescription;
uint256 quantity;
string unit; // TEU, tonnes, pallets
uint256 issuedAt;
ShipmentStatus status;
bytes32 dataHash; // хеш повного документа в IPFS
}
enum ShipmentStatus {
Issued,
InTransit,
ArrivedAtPort,
CustomsCleared,
Delivered,
Surrendered
}
mapping(uint256 => ShipmentData) public shipments;
mapping(uint256 => string[]) public statusHistory; // лог змін статусу
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 може передаватися тільки за певних статусів
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"
);
}
}
}
Ескроу для платежів
Оплата заморожена в смарт-контракті до підтвердження delivery:
contract ShipmentEscrow {
enum EscrowState { Created, Funded, Released, Disputed, Refunded }
struct Escrow {
address buyer;
address seller;
address carrier;
uint256 amount;
address token; // USDC або інший stablecoin
uint256 blTokenId; // ID B/L NFT
address blContract;
EscrowState state;
uint256 releaseDeadline; // якщо немає dispute до deadline — авто-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);
}
}
Блокчейн швидше традиційних баз даних
Блокчейн замінює багатоденну реконсиляцію в Excel та email на єдину захищену книгу. Транзакції підтверджуються за хвилини, а не години. У проекті TradeLens час відвантаження скоротився з 10 днів до 1 дня. Для цього використовується shared ledger з консенсусом PoA (Proof of Authority) на permissioned мережі — висока пропускна здатність (до 1000 TPS) та низька затримка.
Як інтегрувати блокчейн з існуючими TMS?
Логістичні TMS та ERP (SAP, Oracle) мають REST/SOAP API. Наш інтеграційний шар підписує події та відправляє транзакції on-chain:
class LogisticsIntegration {
private web3Provider: Provider;
private blContract: ElectronicBillOfLading;
// Webhook від TMS при зміні статусу вантажу
async handleTMSStatusUpdate(event: TMSEvent) {
const { shipmentId, newStatus, timestamp, operator } = event;
const tokenId = await this.getTokenIdByShipmentId(shipmentId);
const onChainStatus = this.mapTMSStatusToOnChain(newStatus);
// Відправляємо транзакцію
const tx = await this.blContract.updateStatus(
tokenId,
onChainStatus,
`TMS update: ${newStatus} at ${timestamp}`
);
await tx.wait();
// Оновлюємо локальну БД
await this.db.shipments.update({
where: { shipmentId },
data: { lastTxHash: tx.hash, onChainStatus },
});
}
}
Як IoT-телеметрія потрапляє в блокчейн?
Для cold chain (фармацевтика, продукти) важлива верифікація умов зберігання. IoT-сенсори передають дані через шлюз (Raspberry Pi) в смарт-контракт через oracle. Використовуємо Chainlink Functions для децентралізованої агрегації:
contract ShipmentTelemetry {
struct TelemetryRecord {
uint256 timestamp;
int16 temperature; // у десятих частках градуса (156 = 15.6°C)
uint16 humidity; // у десятих відсотках
int32 latitude; // у мікроградусах
int32 longitude;
address oracle; // хто підписав дані
}
mapping(uint256 => TelemetryRecord[]) public telemetry; // tokenId => records
mapping(uint256 => bool) public conditionViolated; // чи були порушення
// Допустимі діапазони для вантажу
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);
}
}
}
Вибір блокчейну: публічний vs приватний vs гібрид
Вибір залежить від вимог до конфіденційності та composability.
| Критерій | Публічний (Polygon, Arbitrum) | Приватний (Hyperledger Fabric) | Гібрид |
|---|---|---|---|
| Доступ | Permissionless | Permissioned | Permissioned + публічні хеші |
| Конфіденційність | Низька (всі бачать) | Висока | Середня |
| Gas | Є | Немає | Немає на приватному шарі |
| Composable з DeFi | Так | Ні | Ні |
| Швидкість транзакцій | ~100-200 TPS | ~1000+ TPS | Залежить від шару |
| Вартість інфраструктури | Низька (публічні ноди) | Висока (свої ноди) | Середня |
Для B2B-консорціуму з відомими учасниками — Hyperledger Fabric. Для відкритого протоколу з токенізацією — Polygon з private transactions.
Порівняння консенсусів для логістики
| Консенсус | Пропускна здатність | Затримка | Енергоспоживання | Приклад |
|---|---|---|---|---|
| PoA | ~1000 TPS | ~1 сек | Низьке | Hyperledger Fabric |
| PoS (Ethereum) | ~15-30 TPS | ~12 сек | Середнє | Ethereum mainnet |
| Tendermint | ~1000 TPS | ~2 сек | Низьке | Cosmos SDK |
| PoW | ~7 TPS | ~10 хв | Високе | Bitcoin (незастосовний) |
Типові помилки при впровадженні
- Ігнорування офчейн-даних: не всі документи потрібно зберігати on-chain, використовуйте IPFS + хеші.
- Відсутність role-based access: хто може випускати B/L, змінювати статус — зашивайте ролі.
- Слабка інтеграція з legacy: без webhook-адаптера TMS залишиться острівцем.
Що входить в роботу (deliverables)
- Архітектурна документація (схеми взаємодії, специфікація контрактів)
- Смарт-контракти з unit-тестами (Foundry / Hardhat)
- Інтеграційний шар (Node.js/Fastify) з API
- Frontend-панель (Next.js + wagmi)
- Changelog та звіт про аудит (Mythril/Slither)
- Навчання операторів (2–4 години)
- Технічна підтримка 3 місяці після релізу
Строки та вартість
Орієнтовні строки:
- MVP (B/L NFT + базовий трекінг + простий escrow) — від 6 тижнів
- Production (IoT, багатосторонній workflow, повноцінна інтеграція) — від 4 місяців
Вартість розраховується індивідуально після аудиту. Залиште заявку — ми зв'яжемося протягом дня. Замовте демонстрацію роботи системи на вашому кейсі — ми покажемо live прототип.
Наш досвід
Ми — команда блокчейн-інженерів з 5+ роками досвіду в Web3. Реалізували понад 10 проектів для логістики, фінтеху та DeFi. Використовуємо лише верифіковані бібліотеки OpenZeppelin, стандарти ERC, та проходимо формальний аудит смарт-контрактів.
Отримайте консультацію: напишіть нам на пошту або в Telegram — покажемо кейси та архітектуру для вашого завдання.







