All contract files could be found in packages/hardhat/contracts
folder.
Contract is divided in several parts, each part is responsible for its own area.
Common logic for 3 entities (Manufacturers, ServiceFactories, PoliceDepartments) is extracted to EntityManagement.sol
class to keep contract size under control. Also there are *Capability.sol
and *Management.sol
parts that are responsible for main role features and role management respectively.
Please note that there is no GovernmentCapability.sol
- Government is only register participants and enable/disable their access, but it does not touch vehicles itself.
Filename | Description |
---|---|
VehicleLifecycleToken.sol |
Main contract / entry point |
EntityManagement.sol |
Common entity management logic |
GovernmentManagement.sol |
Add, Enable/Disable participants |
LifecycleAccessControl.sol |
Roles definition + service functions |
ManufacturerCapability.sol |
ManufactureVehicle + service functions |
PoliceDepartmentCapability.sol |
AddPoliceLogEntry + service functions |
ServiceFactoryCapability.sol |
AddServiceLogEntry + service functions |
ManufacturerManagement.sol |
entity management |
PoliceDepartmentManagement.sol |
entity management |
ServiceFactoryManagement.sol |
entity management |
To optimize contract size in future all *Capabilities
could be converted to separate contracts, so main contract would delegate calls to capability contracts.
There are several design patterns used in this project:
- Inheritance and Interfaces (openzeppelin implementations are used widely)
ERC721URIStorage
- base contract implementation for ERC721 (used for NFT metadata storage + media links)ERC721Enumerable
- base contract implementation for ERC721 (used to simplify UI)Ownable
- access control for contract ownershipCounters
- tracking of number of issued tokensAccessControl
- role based access control for participants (Government, Manufacturers, ServiceFactories, PoliceDepartments)
- Access Control Design Patterns
Ownable
- smart contract ownershipAccessControl
- role based access control for participants
For simplicity all capabilities are implemented as abstract contracts (as final smart contract size fits into threshold), but it is possible to convert them into standalone smart contracts and implement inter-contract communication to allow independent development and deployment of different capabilities while preserving main contract immutable and owning the state.
All public methods are protected by security modifiers related to role or ownership. Ownership is separated from role model.
Owner could be a deployer (or ownership could be transferred to another owner using transferOwnership
method) and it can assign first admin
(Government) that can in turn create other participants.
Roles are assigned explicitly for all participants using setAdmin(addr)
method allowed only for owner, or using add(entityType, addr, name)
allowed for Government role.
All public methods of smart contracts are documented according to NatSpec.
All smart contract functionality is covered with tests.
To run the tests execute the following command from the root of repository:
yarn test
There are 4 different entity types (while only 3 of them are used):
enum EntityType {
UNDEFINED,
MANUFACTURER,
SERVICE_FACTORY,
POLICE
}
Government authority could control access to the system for 3 types of participants: Manufacturers, Service Factories, Police Departments, see EntityType
. For every method entity type should be supplied.
add(entityType, addr, name, metadataUri)
- add new entity to internal storage and grant appropriate role,metadataUri
should be IPFS link (enforced)disable(entityType, addr)
- disable entity and revoke role (it is not deleting entity)enable(entityType, addr)
- enable entity and grant role
All methods are protected by role(GOVERNMENT)
.
manufactureVehicle(vin, make, model, color, year, maxMileage, engineSize, tokenUri)
- creates new vehicle using attributes supplied to the function. This method is protected byrole(MANUFACTURER)
getManufacturers()
- returns list of all manufacturersgetManufacturer(addr)
- return single manufacturer by address
addServiceLogEntry(tokenId, mileage, recordUri)
- add new service log entry,recordUri
should be IPFS link (enforced), method protected byrole(SERVICE_FACTORY)
getServiceLogEntries(tokenId)
- return list of all service log entries for given tokenId
addPoliceLogEntry(tokenId, recordUri)
- add new police log record,recordUri
should be IPFS link (enforced), method protected byrole(POLICE)
getPoliceLogEntries(tokenId)
- return list of all police log entries for given tokenId