| description |
|---|
A description of the smart contracts that rule the DecentraLabs ecosystem |
This repository details the specification of a lab booking, access and sharing decentralized solution using Solidity smart contracts that includes role-based access control using OpenZeppelin libraries.
The proposed architecture leverages a diamond proxy (EIP-2535), behind which various smart contracts are deployed. This approach is chosen for two key reasons:
- To enable seamless updates and enhancements to the contracts without disrupting client functionality.
- To maintain a degree of control during the initial phases of development.
Once the diamond proxy is deployed, the account responsible for the deployment gains the ability to add other accounts, referred to as “providers”. Providers are empowered to list or register online laboratories, which can later be modified, deleted, or transferred to a different provider.
Users, on the other hand, are able to reserve laboratories listed by the providers, as well as cancel any of their existing reservations. Additionally, users can access laboratories they have previously reserved, as long as the reservation remains valid. This functionality is achieved through the implementation of a new proposal: reservable token, which allows for the reservation of the use of an ERC721 token over a time interval, improving upon existing standards.
The implementation of a dedicated payable token, $LAB, through ERC-20, is not covered within the scope of this document. This token is deployed separately from the diamond proxy and a basic specificacion can be found here LabERC20.sol.
The system is divided into multiple facets, each handling specific responsibilities:
ProviderFacet: Handles admin and provider roles.LabFacet: Manages the lab entities.ReservationFacet: Manages reservations/bookings.
- Contract Owner – Holds
DEFAULT_ADMIN_ROLE. Deploys the diamond proxy and initialises facets. Only the owner can grant or revoke provider roles and perform administrative functions. - Provider – Holds
PROVIDER_ROLE. Providers can mint new lab NFTs, update or delete their labs, list/unlist them for reservation, and claim funds from completed bookings. Providers are assigned by the owner using ProviderFacet. - User – Any address that can browse listed labs, request reservations and cancel their own pending bookings. Renters pay the reservation price in $LAB at the time of request.
| Field | Type | Description |
|---|---|---|
| account | address | Wallet address |
| base.name | string | Provider name |
| base.email | string | Email address |
| base.country | string | Country |
| Field | Type | Description |
|---|---|---|
| labId | uint | Unique ID |
| base.uri | string | Off-chain metadata URI |
| base.price | uint96 | Price in $LAB tokens |
| base.auth | string | Authentication service URI |
| base.accessURI | string | Lab services acess URI |
| base.accessKey | string | Key or ID used for routing/access |
| Field | Type | Description |
|---|---|---|
| reservationKey | bytes32 | Unique key associated with the reservation |
| labId | uint | Associated lab ID |
| renter | address | Renter address |
| price | uint96 | Rental price |
| start | uint32 | Start time (Unix epoch) |
| end | uint32 | End time (Unix epoch) |
| status | status | State of the reservation: 0 = PENDING, 1 = BOOKED, 2 = USED, 3 = COLLECTED, 4 = CANCELLED |
Below, each implemented function is listed.
- initialize: Sets up the initial contract state and assigns admin roles
- addProvider: Grants PROVIDER_ROLE to a specified account and mints $LAB tokens.
- removeProvider: Removes the caller’s PROVIDER_ROLE if conditions are met.
- updateProvider: Updates the caller's provider information (name, email, country).
- isLabProvider: Checks if a given account holds the PROVIDER_ROLE.
- getLabProviders: Retrieves a list of all lab providers.
Since ProviderFacet extends OpenZeppelin’s OpenZeppelin’s AccessControlUpgradeable.sol contract, it inherits all role-based access control functionalities provided by the OpenZeppelin library.
- initialize: Sets up the ERC721 token with the provided name and symbol. Initializes the labId to 0.
- addLab: Allows the lab provider to create and register a new lab with metadata securely stored on-chain.
- setTokenURI: Allows the lab provider to set or update the URI for the file containing the off-chain metadata of a specific lab token ID.
- tokenURI: Returns the URI for the off-chain metadata of a given lab/token ID. Used for compliance with ERC721 standards.
- updateLab: Allows the lab provider to update metadata stored on-chain, including the token URI, which links to the metadata stored off-chain.
- deleteLab: Allows the lab provider to delete an existing lab.
- getLab: Retrieves the information about the lab structure (on-chain metadata) corresponding to the provided lab ID.
- getLabsPaginated: Returns a paginated list of lab IDs and the total supply. Limit input must be between 1 and 100.
Since LabFacet extends OpenZeppelin’s OpenZeppelin’s ERC721EnumerableUpgradeable.sol contract, it inherits all the standard ERC-721 enumerable functionalities, including token enumeration and indexing capabilities.
- reservationRequest: Allows a user to request a booking for a lab.
- confirmReservationRequest: The lab provider or authorized account confirms a pending reservation request for a lab.
- denyReservationRequest: Denies a pending reservation request and refunds the payment if necessary.
- cancelReservationRequest: Allows a user to cancel a previously requested reservation and refunds the payment if necessary.
- cancelBooking: Allows a user or the lab provider to cancel an existing confirmed booking.
- requestFunds: Allows lab providers to claim funds from used or expired reservations.
- getLabTokenAddress: Returns the address of the $LAB token contract, set at ProviderFacet initialization.
- getSafeBalance: Retrieves the total balance of $LAB funds held in the contract.
Since ReservationFacet implements the enumerable extension of the newly proposed Reservable Token, as defined in ReservableTokenEnumerable.sol, it also inherits all its functions.
Use case Specification Detailed information on how each specific use case is executed is provided below:
| Description | |
|---|---|
| Use case | INITIALIZE |
| Definition | function initialize(string memory _name, string memory _email, string memory _country, address _labERC20) public initializer |
| Actors | Contract owner |
| Purpose | Initializes the smart contract setting, the initial admin role and the ERC20 token external contract. |
| Summary | Only the contract owner can initialize the contract |
| Preconditions | Have a WALLET and sufficient funds. Can only be executed once. |
| Postconditions | The contract becomes initialized |
| Events | Emits a {RoleGranted} event if the role is successfully granted. |
| Description | |
|---|---|
| Use case | ADD PROVIDER |
| Definition | function addProvider(string memory _name, address _account, string memory _email, string memory _country) external defaultAdminRole |
| Actors | Contract owner |
| Purpose | Adds a new provider by granting the PROVIDER_ROLE and minting tokens for the specified account. |
| Summary | Only the contract owner can add a new provider. |
| Preconditions | The account must not already have the PROVIDER_ROLE. |
| Postconditions | The account receives the PROVIDER_ROLE and 1000 $LAB ERC20 tokens are minted for them. |
| Events | Emits an {ProviderAdded} event if the provider is successfully added. |
| Description | |
|---|---|
| Use case | REMOVE SPECIFIC PROVIDER |
| Definition | function removeProvider(address _provider) external defaultAdminRole |
| Actors | Contract owner |
| Purpose | Removes a specified provider from the provider list if they do not have any lab. |
| Summary | Only the contract owner can remove a provider from the list. |
| Preconditions | The provider must not own any lab. |
| Postconditions | The specified provider's role is revoked if conditions are met. |
| Events | Emits an {ProviderRemoved} event if the provider is successfully removed. |
| Description | |
|---|---|
| Use case | UPDATE PROVIDER |
| Definition | function updateProvider(string memory _name, string memory _email, string memory _country) external onlyRole(PROVIDER_ROLE) |
| Actors | Provider |
| Purpose | Updates the provider information for the caller, modifying their name, email, and country details. |
| Summary | Only a provider can update their own information. |
| Preconditions | The caller must be an existing provider. |
| Postconditions | The provider's information is updated with the new details provided. |
| Events | Emits an {ProviderUpdated} event if the provider is successfully updated. |
The functions listed below are queries that do not modify the state of the variables:
| Function Name | Definition | Purpose | Return Type |
|---|---|---|---|
| isLabProvider | function isLabProvider(address _account) external view returns (bool) |
Checks if the given account is a lab provider. | bool |
| getLabProviders | function getLabProviders() external view returns (Provider[] memory) |
Retrieves the list of all lab providers. | Provider array |
The following table lists the events emitted by ProviderFacet.
| Event | Description | Parameters |
|---|---|---|
ProviderAdded |
Emitted when a new provider is added to the system. |
|
ProviderAddedWithoutTokens |
Emitted when a provider is added but the initial token mint fails (e.g., supply cap reached) |
|
ProviderRemoved |
Emitted when a provider is removed. | (address _account) Address of the provider |
ProviderUpdated |
Emitted when a provider's information is updated. |
|
| Description | |
|---|---|
| Use case | INITIALIZE |
| Definition | function initialize(string memory _name, string memory _symbol) public initializer |
| Actors | Contract owner |
| Purpose | Sets up the ERC721 token with the provided name and symbol, and initializes the labId to 0. |
| Summary | Only the contract owner can initialize the contract |
| Preconditions | Have a WALLET and sufficient funds. Can only be executed once |
| Postconditions | The ERC721 token associated with de labs is initializes |
| Description | |
|---|---|
| Use case | ADD LAB |
| Definition | function addLab(string memory _uri, uint96 _price, string memory _auth, string memory _accessURI, string memory _accessKey) external isLabProvider |
| Actors | Providers |
| Purpose | Allows the contract provider to add a new lab with the on-chain stored metadata |
| Summary | Only a provider can add a new lab |
| Preconditions | Caller must be the lab provider |
| Postconditions | A new lab is registered with the given metadata |
| Events | Emits a {LabAdded} event if successful |
| Description | |
|---|---|
| Use case | SET TOKEN URI |
| Definition | function setTokenURI(uint256 _labId, string memory _tokenURI) external |
| Actors | Providers |
| Purpose | Allows the lab provider to set or update the URI for a specific lab ID |
| Summary | Only the lab provider can modify the lab URI |
| Preconditions | Caller must be the lab provider; the lab ID must exist |
| Postconditions | The specified lab's URI is updated |
| Events | Emits a {LabURISet} event if successful |
| Description | |
|---|---|
| Use case | UPDATE LAB |
| Definition | function updateLab(uint _labId, string memory _uri, uint96 _price, string memory _auth, string memory _accessURI, string memory _accessKey) external onlyLabProvider(_labId) |
| Actors | Provider |
| Purpose | Allows the lab provider to update the on-chain stored metadata of an existing lab |
| Summary | Only the lab provider can modify lab details |
| Preconditions | Caller must be the lab provider; lab ID must exist |
| Postconditions | The specified lab's metadata are updated |
| Events | Emits a {LabUpdated} event if successful |
| Description | |
|---|---|
| Use case | DELETE LAB |
| Definition | function deleteLab(uint _labId) external onlyLabProvider(_labId) |
| Actors | Providers |
| Purpose | Allows the lab provider to delete an existing lab |
| Summary | Only the lab provider can remove a lab |
| Preconditions | Caller must be the lab provider; lab ID must exist and be removable |
| Postconditions | The specified lab is removed from the system |
| Events | Emits a {LabDeleted} event if successful |
The functions listed below are queries that do not modify the state of the variables:
| Function Name | Definition | Purpose | Return Type |
|---|---|---|---|
| getLab | function getLab(uint _labId) public view returns (Lab memory) |
Retrieves the lab associated with the given lab ID. | Lab |
| getAllLabs | function getAllLabs() public view returns (uint256[] memory) |
Retrieves the list of the all labs ID. | ID (uint256) array |
| tokenURI | function tokenURI(uint256 _labId) public view returns (string memory) |
Retrieves the URI associated with a specific lab ID. | URI string |
The following table lists the events emitted by the LabFacet.
| Event | Description | Parameters |
|---|---|---|
LabAdded |
Emitted when a new lab is added to the system. |
|
LabUpdated |
Emitted when a lab is updated. |
|
LabDeleted |
Emitted when a lab is deleted. |
|
LabURISet |
Emitted when the URI of a lab is set. |
|
| Description | |
|---|---|
| Use case | RESERVATION REQUEST |
| Definition | function reservationRequest(uint256 _labId, uint32 _start, uint32 _end) external exists(_labId) override |
| Actors | Users |
| Purpose | Initiates a new reservation request |
| Summary | Creates a reservation request with specified details |
| Preconditions | The lab must exists |
| Postconditions | Reservation state is updated to PENDING |
| Events | {ReservationRequested} |
| Description | |
|---|---|
| Use case | CONFIM RESERVATION REQUEST |
| Definition | function confimReservationRequest(bytes32 _reservationKey) external defaultAdminRole reservationPending(_reservationKey) override |
| Actors | Providers or authorized account |
| Purpose | Confirm and book the reservation |
| Summary | Creates a reservation request with specified details |
| Preconditions | Caller must be the lab provider (for now, the DEFAULT_ADMIN_ROLE) |
| Postconditions | State is updated to BOOKED |
| Events | Emits a {ReservationConfirmed} event if successful |
| Description | |
|---|---|
| Use case | DENY RESERVATION REQUEST |
| Definition | function denyReservationRequest(bytes32 _reservationKey) external defaultAdminRole reservationPending(_reservationKey) override |
| Actors | Providers or authorized account |
| Purpose | Denies a pending reservation request |
| Summary | Reservation request is marked as denied |
| Preconditions | Caller must be the lab provider (for now, the DEFAULT_ADMIN_ROLE) |
| Postconditions | State is updated to CANCELLED |
| Events | Emits a {ReservationRequestDenied} event if successful |
| Description | |
|---|---|
| Use case | CANCEL RESERVATION REQUEST |
| Definition | function cancelReservationRequest(bytes32 _reservationKey) external override |
| Actors | Users |
| Purpose | Allows cancellation of an existing reservation |
| Summary | Cancels an active reservation |
| Preconditions | Caller must be the renter |
| Postconditions | State is updated to CANCELLED |
| Events | Emits a {ReservationRequestCanceled} event if successful |
| Description | |
|---|---|
| Use case | CANCEL BOOKING |
| Definition | function cancelBooking(bytes32 _reservationKey) external override |
| Actors | Users, providers |
| Purpose | Allows cancellation of an existing booking |
| Summary | Cancels an active booking |
| Preconditions | Caller must be the renter or the lab provider |
| Postconditions | State is updated to CANCELLED |
| Events | Emits a {BookingCanceled} event if successful |
| Description | |
|---|---|
| Use case | REQUEST FUNDS |
| Definition | function requestFunds() external isLabProvider |
| Actors | Providers |
| Purpose | Allows lab providers to claim funds from used or expired reservations |
| Summary | Creates a reservation request with specified details |
| Preconditions | Caller must be a registered lab provider |
| Postconditions | Transfers the total amount of $LAB tokens from all eligible reservations to the provider |
| Events | None |
The functions listed below are queries that do not modify the state of the variables:
| Function Name | Definition | Purpose | Return Type |
|---|---|---|---|
| getLabTokenAddress | function getLabTokenAddress() external view returns (address) |
Returns the address of the $LAB ERC20 token | |
| getSafeBalance | function getSafeBalance() public view returns (uint256) |
Returns the current balance of Lab tokens held by this contract | uint256 |
The following table lists the events emitted by the ProviderFacet.
| Event | Description | Parameters |
|---|---|---|
ReservationRequested |
Emitted when a user submits a new reservation request. |
|
ReservationConfirmed |
Emitted when a reservation request is confirmed. |
|
ReservationRequestDenied |
Emitted when a reservation request is denied. |
|
ReservationRequestCanceled |
Emitted when a reservation request is canceled by the user. |
|
BookingCanceled |
Emitted when a confirmed booking is canceled. |
|
LabListed |
Emitted when a lab is listed |
|
LabUnlisted |
Emitted when a lab is unlisted |
|
The project uses a dedicated ERC-20 token: $LAB for all transactions. The token is deployed outside the diamond proxy.