Skip to content

Commit 7da3414

Browse files
authored
Merge pull request #10 from dadadave80:feat/supply-chain-logic
Feat/supply-chain-logic
2 parents 429531e + e3c5f85 commit 7da3414

17 files changed

+1582
-138
lines changed

HTS-LIBS.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# hedera-diamond-hts-lib
2+
3+
_Seamless Hedera Token Service (HTS) integration for EIP-2535 Diamond Standard smart contracts._
4+
5+
---
6+
7+
## Overview
8+
9+
**hedera-diamond-hts-lib** solves the challenge of using Hedera's non-EVM HTS precompiles and abstract contracts (like `IHederaTokenService`) within modular, upgradeable Diamond (EIP-2535) architectures. Instead of inheriting abstract contracts (which is incompatible with delegatecall-based facets), this library provides gas-optimized, composable `library` wrappers for all core HTS operations.
10+
11+
These libraries enable facets in a Diamond proxy to perform mint, burn, transfer, associate, dissociate, freeze, KYC, and other token operations directly, using delegatecall-compatible stateless functions. This unlocks full HTS utility in modular, upgradable dApps.
12+
13+
---
14+
15+
## Features
16+
17+
- **Modular HTS Operations:** Each HTS function (mint, burn, transfer, etc.) is available as a standalone library function.
18+
- **Diamond Facet Compatible:** Designed for use inside EIP-2535 facets via delegatecall, with no stateful logic.
19+
- **Full HTS Coverage:** Supports association, dissociation, KYC, freeze, custom fees, NFT and fungible operations, and more.
20+
- **Lightweight & Composable:** No inheritance, minimal overhead, easily composed with other libraries and storage patterns.
21+
22+
---
23+
24+
## Installation
25+
26+
### Prerequisites
27+
- Foundry or hardhat project
28+
29+
### Add to Your Project
30+
31+
#### Option 1: Manual Copy
32+
Copy the `.sol` files from `src/libraries/hts/` into your project's libraries directory.
33+
34+
#### Option 2: forge install
35+
```sh
36+
forge install dadadave80/chronicle
37+
```
38+
39+
---
40+
41+
## Usage Example
42+
43+
Import and use in a facet contract:
44+
45+
```solidity
46+
import {LibHederaTokenService} from "@chronicle/libraries/hts/LibHederaTokenService.sol";
47+
48+
contract ProductsFacet {
49+
function mintProduct(address token, int64 amount) external {
50+
// Calls the Hedera Token Service mint via precompile
51+
(int256 code, int64 newSupply, int64[] memory serials) = LibHederaTokenService.mintToken(token, amount, new bytes[](0));
52+
require(code == 22, "Mint failed"); // 22 = SUCCESS
53+
}
54+
}
55+
```
56+
57+
**Best Practices:**
58+
- Use with [LibDiamond](https://eips.ethereum.org/EIPS/eip-2535) storage patterns—never store state in libraries.
59+
- Use `using LibHederaTokenService for address;` for more ergonomic syntax.
60+
- Combine with access control and event logging as needed.
61+
62+
---
63+
64+
## Project Structure
65+
66+
- `LibHederaTokenService.sol` — Core stateless wrappers for all HTS precompile operations (mint, burn, transfer, associate, etc.)
67+
- `LibSafeHTS.sol` — Revert-on-failure safe wrappers for all HTS calls (throws on non-SUCCESS response).
68+
- `LibFeeHelper.sol` — Utilities for constructing custom fee structs for HTS tokens.
69+
- `LibKeyHelper.sol` — Utilities for managing HTS key types and key assignment.
70+
71+
---
72+
73+
## Limitations / Considerations
74+
75+
- **Delegatecall Overhead:** Calls from facets via delegatecall are slightly more expensive than direct contract calls.
76+
- **No Library State:** All logic must be stateless; use Diamond storage patterns for persistent data.
77+
- **HTS Compatibility:** Only works on Hedera-compatible EVM chains with HTS precompiles available at `0x167`.
78+
- **Error Handling:** Use `LibSafeHTS` for automatic revert on failure, or handle response codes manually with `LibHederaTokenService`.
79+
80+
---
81+
82+
## Testing
83+
84+
- **Unit Tests:**
85+
- Hardhat: `npx hardhat test`
86+
- Foundry: `forge test`
87+
- **Coverage:**
88+
- Hardhat: `npx hardhat coverage`
89+
- Foundry: `forge coverage`
90+
91+
Tests cover all core HTS operations, including edge cases and error handling.
92+
93+
---
94+
95+
## Contributing
96+
97+
- Fork and submit PRs for new HTS methods, optimizations, or bug fixes.
98+
- Adhere to Solidity style guidelines and include unit tests for new features.
99+
- Open issues for feature requests or HTS compatibility questions.
100+
101+
---
102+
103+
## License
104+
105+
MIT
106+
107+
---
108+
109+
## References
110+
111+
- [Hedera Token Service Documentation](https://docs.hedera.com/hedera/smart-contracts/hedera-token-service)
112+
- [EIP-2535 Diamond Standard](https://eips.ethereum.org/EIPS/eip-2535)
113+
- [Hedera Token Service Solidity Interfaces](https://github.com/hashgraph/hedera-smart-contracts)

src/facets/ProductsFacet.sol

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,32 @@ import {Product} from "@chronicle-types/ProductStorage.sol";
77
contract ProductsFacet {
88
using LibProduct for *;
99

10-
function addProduct(string calldata _name, string calldata _memo, int64 _price, int64 _initialSupply) external {
11-
_name._addProduct(_memo, _price, _initialSupply);
10+
function addProduct(
11+
string calldata _name,
12+
string calldata _memo,
13+
int64 _price,
14+
int64 _transporterFee,
15+
int64 _initialSupply
16+
) external {
17+
_name._addProduct(_memo, _price, _transporterFee, _initialSupply);
1218
}
1319

14-
function updateProduct(address _tokenAddress, string calldata _name, string calldata _memo, int64 _price)
15-
external
16-
{
17-
_tokenAddress._updateProduct(_name, _memo, _price);
20+
function updateProduct(
21+
address _tokenAddress,
22+
string calldata _name,
23+
string calldata _memo,
24+
int64 _price,
25+
int64 _transporterFee
26+
) external {
27+
_tokenAddress._updateProduct(_name, _memo, _price, _transporterFee);
1828
}
1929

2030
function increaseProductQuantity(address _tokenAddress, int64 _quantity) external {
2131
_tokenAddress._increaseProductQuantity(_quantity);
2232
}
2333

24-
function decreaseProductQuantity(address _tokenAddress, int64 _quantity, int64[] memory _serialNumbers) external {
25-
_tokenAddress._decreaseProductQuantity(_quantity, _serialNumbers);
34+
function decreaseProductQuantity(address _tokenAddress, int64 _quantity) external {
35+
_tokenAddress._decreaseProductQuantity(_quantity);
2636
}
2737

2838
function getProductByTokenAddress(address _tokenAddress) public view returns (Product memory) {
@@ -45,11 +55,11 @@ contract ProductsFacet {
4555
return LibProduct._getProductsByRange(_start, _end);
4656
}
4757

48-
function getSupplierProductTokenAddresses(address _owner) public view returns (address[] memory) {
49-
return _owner._getSupplierProductTokenAddresses();
58+
function getSupplierProductTokenAddresses(address _supplier) public view returns (address[] memory) {
59+
return _supplier._getSupplierProductTokenAddresses();
5060
}
5161

52-
function getSupplierProducts(address _owner) public view returns (Product[] memory) {
53-
return _owner._getSupplierProducts();
62+
function getSupplierProducts(address _supplier) public view returns (Product[] memory) {
63+
return _supplier._getSupplierProducts();
5464
}
5565
}

src/facets/SupplyChainFacet.sol

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.30;
3+
4+
import {LibSupplyChain} from "@chronicle/libraries/LibSupplyChain.sol";
5+
import {SupplyChainStatus} from "@chronicle-types/SupplyChainStorage.sol";
6+
import {Product} from "@chronicle-types/ProductStorage.sol";
7+
8+
contract SupplyChainFacet {
9+
using LibSupplyChain for address;
10+
11+
function retailerOrderProduct(address _productToken, int64 _quantity) external payable {
12+
_productToken._retailerOrderProduct(_quantity);
13+
}
14+
15+
function transporterSelectOrder(address _productToken, int64 _quantity) external payable {
16+
_productToken._transporterSelectOrder(_quantity);
17+
}
18+
19+
function transporterUpdateStatus(address _productToken, SupplyChainStatus _status) external {
20+
_productToken._transporterUpdateStatus(_status);
21+
}
22+
23+
function retailerReceiveProduct(address _productToken, int64 _quantity) external payable {
24+
_productToken._retailerReceiveProduct(_quantity);
25+
}
26+
27+
function getActiveDeliveries() external view returns (address[] memory) {
28+
return LibSupplyChain._getAllActiveDeliveriesAddress();
29+
}
30+
31+
function getAllActiveDeliveries() external view returns (Product[] memory) {
32+
return LibSupplyChain._getAllActiveDeliveries();
33+
}
34+
35+
function getRetailerOrders(address _retailer) external view returns (address[] memory) {
36+
return LibSupplyChain._getRetailerOrders(_retailer);
37+
}
38+
39+
function getTransporterOrders(address _transporter) external view returns (address[] memory) {
40+
return LibSupplyChain._getTransporterOrders(_transporter);
41+
}
42+
43+
function getSupplierOrders(address _supplier) external view returns (address[] memory) {
44+
return LibSupplyChain._getSupplierOrders(_supplier);
45+
}
46+
47+
function getProductRetailers(address _productToken) external view returns (address[] memory) {
48+
return LibSupplyChain._getProductRetailers(_productToken);
49+
}
50+
51+
function getProductTransporter(address _productToken) external view returns (address) {
52+
return LibSupplyChain._getProductTransporter(_productToken);
53+
}
54+
55+
function getProductSuppliers(address _productToken) external view returns (address[] memory) {
56+
return LibSupplyChain._getProductSuppliers(_productToken);
57+
}
58+
59+
function getProductSupplyChainStatus(address _productToken) external view returns (SupplyChainStatus) {
60+
return LibSupplyChain._getProductSupplyChainStatus(_productToken);
61+
}
62+
}

src/libraries/LibParty.sol

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import {
77
} from "@chronicle-types/PartyStorage.sol";
88
import {LibContext} from "@chronicle/libraries/LibContext.sol";
99
import {LibOwnableRoles} from "@diamond/libraries/LibOwnableRoles.sol";
10+
/// forge-lint: disable-next-line(unaliased-plain-import)
1011
import "@chronicle-logs/PartyLogs.sol";
12+
/// forge-lint: disable-next-line(unaliased-plain-import)
13+
import "@chronicle/libraries/errors/PartyErrors.sol";
1114

1215
library LibParty {
1316
using EnumerableSet for EnumerableSet.AddressSet;
@@ -27,9 +30,9 @@ library LibParty {
2730
function _registerParty(string calldata _name, Role _role) internal {
2831
PartyStorage storage $ = _partyStorage();
2932
address sender = LibContext._msgSender();
30-
if ($.parties[sender].frozen) revert("Party frozen");
31-
if (!$.roles[_role].add(sender)) revert("Role already exists");
32-
if (!$.activeParties.add(sender)) revert("Party already exists");
33+
if ($.parties[sender].frozen) revert PartyFrozen(sender);
34+
if (!$.roles[_role].add(sender)) revert RoleAlreadyExists(_role);
35+
if (!$.activeParties.add(sender)) revert PartyAlreadyExists(sender);
3336
Party memory party =
3437
Party({name: _name, addr: sender, role: _role, active: true, frozen: false, rating: Rating.Zero});
3538
$.parties[sender] = party;
@@ -39,8 +42,8 @@ library LibParty {
3942
function _deactivateParty(Role _role) internal {
4043
PartyStorage storage $ = _partyStorage();
4144
address sender = LibContext._msgSender();
42-
if (!$.activeParties.remove(sender)) revert("Party not active");
43-
if (!$.roles[_role].remove(sender)) revert("Role not found");
45+
if (!$.activeParties.remove(sender)) revert PartyNotActive(sender);
46+
if (!$.roles[_role].remove(sender)) revert RoleNotFound(_role);
4447
delete $.parties[sender].active;
4548
emit PartyDeactivated($.parties[sender]);
4649
}

0 commit comments

Comments
 (0)