This commit is contained in:
writer 2024-10-15 11:39:29 +09:00
parent 49c2206dc1
commit 5fbab7ede1
13 changed files with 808 additions and 28 deletions

View File

@ -1,38 +1,33 @@
## ⛓🛠 blockchain data engineering
## ⛓🛠 blockchain infrastructure && data engineering
<br>
##### 👉 this repository contains some pf my blockchain engineering projects such as scalable event scanners and infrastructure setups for on-chain analysis and machine learning models training.
##### 🛠 here is a high-level system design chart for a possible blockchain intelligence data platform (all deployed in kubernetes):
<br>
<p align="center">
<img src="https://user-images.githubusercontent.com/1130416/224561453-274c5066-240d-4cc5-b63b-b4c57388a0e0.png" width="70%" align="center" style="padding:1px;border:1px solid black;"/>
<br>
<br>
---
### in this repo
##### 👉 this repository holds some of my blockchain infrastructure MVPs (such as scalable event scanners and infrastructure setups for on-chain analysis and machine learning models training)
<br>
* **[token-scanner-api](token-scanner-api)**:
- a mvp for a **scalable event scanner cli and api for ethereum**, through indexing and parsing blocks events. this is the first step for training **machine learning models on the chains** (e.g., high-frequency trading with deep learning).
- check my mirror post **[on building a scalable event scanner for ethereum](https://mirror.xyz/steinkirch.eth/vSF18xcLyfXLIWwxjreRa3I_XskwgnjSc6pScegNJWI)**.
* **[apache arrow](technologies/arrow_project.md)**
* **[rlp enconding](technologies/rlp_enconding.md)**
* **[spotify's luigi](technologies/luigi.md)**
* **[google's or-tools](technologies/or_tools.md)**
- a mvp for a **scalable event scanner cli and api for ethereum**, through indexing and parsing blocks events. this is the first step for training **machine learning models on the chains** (e.g., high-frequency trading with deep learning)
- check my mirror post **[on building a scalable event scanner for ethereum](https://mirror.xyz/go-outside.eth/vSF18xcLyfXLIWwxjreRa3I_XskwgnjSc6pScegNJWI)**
* **[implementation of a draft of an EIP for rental NFTs with rights management](eip-9999)**:
- this standard is an extension of ERC-721 and ERC-4907
- it proposes an API for rights management through a rental license
- this framework is fully compatible with the licensing framework proposed on ERC-5218, although it can be implemented independently
* **[a protocol for management and bartering of game (NFT) assets](game-protocol-design)**:
- discussion, design, and implementation route of a protocol for management and bartering of game (NFT) assets
* **research notes**:
- **[apache arrow](technologies/arrow_project.md)**
- **[rlp encoding](technologies/rlp_enconding.md)**
- **[spotify's luigi](technologies/luigi.md)**
- **[google's or-tools](technologies/or_tools.md)**
<br>
@ -43,7 +38,14 @@
<br>
* **[go-outside-labs ml-hft-agents](https://github.com/go-outside-labs/hft-deep-learning-agents)**
* **[go-outside-labs orchestration-toolkit](https://github.com/go-outside-labs/orchestration-toolkit)**
* **[urani trade experiments (on solana)](https://github.com/urani-trade)**:
- a batching orderbook on mongodb: **[orderbook-poc-ts](https://github.com/urani-trade/orderbook-poc-ts)**
- a swap protocol for order intents: **[urani-swap-ts](https://github.com/urani-trade/urani-swap-ts)**
- a mev solver: **[solana-mev-agent-py](https://github.com/urani-trade/solana-mev-agent-py)**
- a graph visualization of order fulfillment: **[urani-arena-graph-ts](https://github.com/urani-trade/urani-arena-graph-ts)**
* **[autistic symposium's ml-ai-agents-py](https://github.com/autistic-symposium/ml-ai-agents-py)**
* **[autistic symposium's backend-and-orchestration-toolkit](https://github.com/autistic-symposium/backend-and-orchestration-toolkit)**
* **[google biquery article on blockchain public datasets](https://cloud.google.com/blog/products/data-analytics/introducing-six-new-cryptocurrencies-in-bigquery-public-datasets-and-how-to-analyze-them)**
* **[paradigm's data portal](https://data.paradigm.xyz/)**

33
eip-9999/README.md Normal file
View File

@ -0,0 +1,33 @@
## implementation of a draft of an EIP for rental NFTs with rights management
<br>
* please refer to [this link](eip-9999.md) for the proposal, which follows the guidelines from the [ethereum foundation](https://github.com/ethereum/EIPs).
* a reference implementation (in [foundry](https://book.getfoundry.sh/)) can be found [here](eip-9999-test/).
* finally, you can check that the EIP is well-formatted by running EF's EIP validator locally:
<br>
```shell
> cargo install eipv
> eipv
draft: 1, review: 0, last_call: 0, final: 0, stagnant: 0, withdrawn: 0, living: 0
valid: 1, invalid: 0
```
<br>
----
### references
* [EF's list of ERCs](https://eips.ethereum.org/erc)
* [EIP-5218, NFT rights management](https://eips.ethereum.org/EIPS/eip-5218)
* [ERC-4907, Rental NFT, an extension of EIP-721](https://eips.ethereum.org/EIPS/eip-4907)
* [the token-bound license, by james grimmelmann](https://eips.ethereum.org/assets/eip-5218/ic3license/ic3license.pdf)
* [how do EIP numbers get assigned?](https://ethereum-magicians.org/t/how-do-eip-numbers-get-assigned/9079/4)
* [difference between ERC and EIP](https://ethereum.stackexchange.com/questions/44847/difference-and-relationship-if-any-between-erc-and-eip)
* [running the EIP validator locally](https://github.com/ethereum/EIPs/tree/master?tab=readme-ov-file)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -0,0 +1,13 @@
## Reference Implementation for EIP-9999
A reference implementation can be found at `src/`.
A very simple test can be found at `test/` and requires [Foundry](https://book.getfoundry.sh/) to be installed.
Run with:
```shell
> forge test -vvvv
```

View File

@ -0,0 +1,6 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
# See more config options https://github.com/foundry-rs/foundry/tree/master/config

View File

@ -0,0 +1,5 @@
{
"dependencies": {
"@openzeppelin/contracts": "^5.0.1"
}
}

View File

@ -0,0 +1,111 @@
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.16;
import "forge-std/Test.sol";
import "./IERC9999.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
contract ERC9999 is IERC9999, Ownable {
struct UserRentalInfo {
address user;
uint64 expires;
uint256 licenseId;
}
struct RentalLicense {
uint256 tokenId;
uint256 parentLicenseId;
string uri;
}
mapping (uint256 => UserRentalInfo) private _users; // maps from an NFT to its user, expiration, and rental license identifier
mapping (uint256 => RentalLicense) private _licenses; // maps from a rental license identifier to a rental license struct object
mapping (uint256 => uint256) private _licenseIds; // maps from an NFT to its rental license identifier
/// @notice Get a user's rental license.
/// @dev The zero value indicates no license.
/// Throws if `tokenId` is not a valid NFT.
/// @param tokenId The NFT to get the rental license for.
/// @return licenseId The identifier of the rental license.
function userRentalLicense(uint256 tokenId) external view returns(uint256) {
require(tokenId != 0, "ERC9999: tokenId is not valid");
return _users[tokenId].licenseId;
}
/// @notice Set the temporary user, expires, and rental license of an NFT.
/// @dev The zero address indicates that the rental NFT has no user.
/// Throws if `tokenId` is not valid NFT.
/// Throws if `licenseId` is not a valid license.
/// Throws if `msg.sender` is not the owner of the NFT.
/// Throws if `expires` is in the past.
/// @param tokenId The NFT to be rented.
/// @param user The new user of the NFT.
/// @param licenseId The identifier of the rental license.
/// @param expires UNIX timestamp the new user can use the NFT before it expires.
function setUserRentalLicense(uint256 tokenId, address user, uint256 licenseId, uint64 expires) external onlyOwner {
require(tokenId != 0, "ERC9999: tokenId is not valid");
require(licenseId != 0, "ERC9999: licenseId is not valid");
require(expires > block.timestamp, "ERC9999: expires is not valid");
_users[tokenId] = UserRentalInfo(user, expires, licenseId);
emit UpdateRentalLicense(tokenId, licenseId, user, expires);
}
/// @notice Create a new rental license.
/// @dev The zero value for parentLicenseId indicates the license has no parent.
/// Throws if `tokenId` is not valid NFT.
/// Throws if `msg.sender` is not the owner of the NFT.
/// Throws uri is invalid.
/// @param tokenId The NFT the rental license is issued upon.
/// @param parentLicenseId The identifier for the parent license.
/// @param uri The URI of the license terms.
/// @return licenseId The identifier of the created rental license.
function createRentalLicense(uint256 tokenId, uint256 parentLicenseId, string memory uri) external onlyOwner returns (uint256) {
require(tokenId != 0, "ERC9999: tokenId is not valid");
require(bytes(uri).length != 0, "ERC9999: uri is not valid");
uint256 licenseId = _getNextLicenseId();
_licenses[licenseId] = RentalLicense(tokenId, parentLicenseId, uri);
_licenseIds[tokenId] = licenseId;
emit CreateRentalLicense(licenseId, tokenId, parentLicenseId, uri);
return licenseId;
}
/// @notice Get the next rental license identifier.
/// @dev Throws if the identifier overflows.
/// @return licenseId The next rental license identifier.
function _getNextLicenseId() private view returns (uint256) {
uint256 _licenseIdTracker = 1;
while (true) {
if (_licenseIds[_licenseIdTracker] == 0) {
break;
}
_licenseIdTracker++;
}
require(_licenseIdTracker < 2**256 - 1, "ERC9999: licenseId overflow");
return _licenseIdTracker;
}
/// @notice Get the URI of a rental license.
/// @dev Throws if `licenseId` is not a valid license.
/// @param licenseId The identifier of the rental license.
/// @return uri The URI of the rental license.
function getLicenseURI(uint256 licenseId) external view returns (string memory) {
require(licenseId != 0, "ERC9999: licenseId is not valid");
return _licenses[licenseId].uri;
}
}

View File

@ -0,0 +1,44 @@
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;
/// @title ERC-9999: Rental NFTs with Rights Management
interface IERC9999 /* is IERC721, IERC165 */{
/// @dev Emits when a rental license is set to a rental NFT.
/// The zero address for the user indicates that there is no user address.
event UpdateRentalLicense(uint256 tokenId, uint256 licenseId, address user, uint64 expires);
/// @dev Emits when a new rental license is created.
/// The zero value for `parentLicenseId` indicates there is no parent license.
event CreateRentalLicense(uint256 licenseId, uint256 tokenId, uint256 parentLicenseId, string uri);
/// @notice Get a user's rental license.
/// @dev The zero value indicates no license.
/// Throws if `tokenId` is not a valid NFT.
/// @param tokenId The NFT to get the rental license for.
/// @return licenseId The identifier of the rental license.
function userRentalLicense(uint256 tokenId) external view returns(uint256);
/// @notice Set the temporary user, expires, and rental license of an NFT.
/// @dev The zero address indicates that the rental NFT has no user.
/// Throws if `tokenId` is not valid NFT.
/// Throws if `licenseId` is not a valid license.
/// Throws if `msg.sender` is not the owner of the NFT.
/// Throws if `expires` is in the past.
/// @param tokenId The NFT to be rented.
/// @param user The new user of the NFT.
/// @param licenseId The identifier of the rental license.
/// @param expires UNIX timestamp the new user can use the NFT before it expires.
function setUserRentalLicense(uint256 tokenId, address user, uint256 licenseId, uint64 expires) external;
/// @notice Create a new rental license.
/// @dev The zero value for parentLicenseId indicates the license has no parent.
/// Throws if `tokenId` is not valid NFT.
/// Throws if `msg.sender` is not the owner of the NFT.
/// Throws uri is invalid.
/// @param tokenId The NFT the rental license is issued upon.
/// @param parentLicenseId The identifier for the parent license.
/// @param uri The URI of the license terms.
/// @return licenseId The identifier of the created rental license.
function createRentalLicense(uint256 tokenId, uint256 parentLicenseId, string memory uri) external returns (uint256);
}

View File

@ -0,0 +1,42 @@
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol";
import {ERC9999} from "../src/erc9999.sol";
contract ERC9999Test is Test {
ERC9999 e;
address user = address(0xbeef);
string tokenURI = "someTokenURI";
string licenseURI = "someLicenseURI";
uint256 tokenId = 1;
uint256 parentLicenseId = 0;
uint64 expires = 1737586800;
event CreateRentalLicense(uint256 licenseId, uint256 tokenId, uint256 parentLicenseId, string uri);
event UpdateRentalLicense(uint256 tokenId, uint256 licenseId, address user, uint64 expires);
function setUp() public {
vm.deal(user, 10 ether);
e = new ERC9999();
}
function testERC9999() public {
// Test license creation (createRentalLicense)
vm.expectEmit();
emit CreateRentalLicense(1, tokenId, parentLicenseId, licenseURI);
uint256 licenseId = e.createRentalLicense(tokenId, parentLicenseId, licenseURI);
assertEq(e.getLicenseURI(licenseId), licenseURI, "License URI should match");
// Test setting userRentalLicense
vm.expectEmit();
emit UpdateRentalLicense(tokenId, licenseId, user, expires);
e.setUserRentalLicense(tokenId, user, licenseId, expires);
// Test license lookup (userRentalLicense)
assertEq(e.userRentalLicense(tokenId), licenseId, "LicenseId should match");
}
}

328
eip-9999/eip-9999.md Normal file
View File

@ -0,0 +1,328 @@
---
eip: 9999
title: Rental NFTs with Rights Management
status: Draft
type: Standards Track
category: ERC
author: mia stein
discussions-to: https://storyprotocol.xyz
---
<br>
## Abstract
This standard is an extension of **ERC-721** and **ERC-4907**. It proposes an API for rights management through a rental license. This framework is fully compatible with the licensing framework proposed on **ERC-5218**, although it can be implemented independently.
As **ERC-4907** adds a user role to be granted to addresses with an expiration date (when the rental is over), **EIP-9999** extends the framework to allow the creation of a rental license that can be temporarily bound to the NFT `tokenId` until its `expires` UNIX timestamp ends. The rental license is defined through a URI for the license terms and an optional `parentLicenseId`, as per **ERC-5218**.
<br>
## Motivation
NFTs can have several utilities, from game utilities to creative work or real-world assets.
Permission management becomes essential as NFT owners might be interested in temporarily renting a token, permitting a "user" specific "using" rights while disallowing others, or simply allowing temporary access to the NFT. In addition, echoing the "real world," different roles might carry different rights conveyed by licensing documents. Thus, a legal mechanism to map the on-chain asset to the off-chain privileges is desirable.
**ERC-4907** introduced a framework for an additional role (rental user) that can be granted to their `address` with an expiration timestamp. For instance, the user role represents permission to "use" the NFT but not the ability to transfer it or set users. By introducing an `expires` attribute, the `owner` no longer needs to submit two on-chain transactions (one to list a new address as the new user role at the start of the rental and one to reclaim the user role at the end), but rather, can rely on the automatic end of a usage term without the need for a second transaction.
In parallel, **ERC-5218** introduced an API standard for managing NFT rights through a new primitive, `licenseId,` which maps a `tokenId` to a URI with a `JSON` standard containing a reference to a license (consisting of human-readable legal code and/or machine-readable summaries). We leverage the same standards for the simpler rental use case. Although our standard does not require the specification of sublicenses or revocation, the **ERC-5218** API could still be leveraged to create our temporary rental licenses.
By expanding **ERC-4907**'s API to support rental rights management and by supporting the framework introduced by **ERC-5218**, we introduce a simplified and rental-specific bridge for these standards.
<br>
## Specification
The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
Every **ERC-9999** compliant contract MUST implement the `IERC9999` interface:
```solidity
pragma solidity ^0.8.0;
/// @title ERC-9999: Rental NFTs with Rights Management
interface IERC9999 {
/// @dev Emits when a rental license is set to a rental NFT.
/// The zero address for the user indicates that there is no user address.
event UpdateRentalLicense(uint256 tokenId, uint256 licenseId, address user, uint64 expires);
/// @dev Emits when a new rental license is created.
/// The zero value for `parentLicenseId` indicates there is no parent license.
event CreateRentalLicense(uint256 licenseId, uint256 tokenId, uint256 parentLicenseId, string uri);
/// @notice Get a user's rental license.
/// @dev The zero value indicates no license.
/// Throws if `tokenId` is not a valid NFT.
/// @param tokenId The NFT to get the rental license for.
/// @return licenseId The identifier of the rental license.
function userRentalLicense(uint256 tokenId) external view returns(uint256);
/// @notice Set the temporary user, expires, and rental license of an NFT.
/// @dev The zero address indicates that the rental NFT has no user.
/// Throws if `tokenId` is not valid NFT.
/// Throws if `licenseId` is not a valid license.
/// Throws if `msg.sender` is not the owner of the NFT.
/// Throws if `expires` is in the past.
/// @param tokenId The NFT to be rented.
/// @param user The new user of the NFT.
/// @param licenseId The identifier of the rental license.
/// @param expires UNIX timestamp the new user can use the NFT before it expires.
function setUserRentalLicense(uint256 tokenId, address user, uint256 licenseId, uint64 expires) external;
/// @notice Create a new rental license.
/// @dev The zero value for parentLicenseId indicates the license has no parent.
/// Throws if `tokenId` is not valid NFT.
/// Throws if `msg.sender` is not the owner of the NFT.
/// Throws uri is invalid.
/// @param tokenId The NFT the rental license is issued upon.
/// @param parentLicenseId The identifier for the parent license.
/// @param uri The URI of the license terms.
/// @return licenseId The identifier of the created rental license.
function createRentalLicense(uint256 tokenId, uint256 parentLicenseId, string memory uri) external returns (uint256);
}
```
<br>
The event `UpdateRentalLicense` MUST be emitted when a user `address` is changed, or the user `expires` is changed, or a `licenseId` is changed.
The `CreateRentalLicense` event MUST be emitted when a new rental license is created.
The rental license MUST be compatible with `ERC-721`.
The license URI MAY point to a `JSON` file that conforms to the **ERC-5218 Metadata JSON Schema** as below, which adopts the "three-layer" design of the Creative Commons Licenses:
```json
{
"title": "Rental License Metadata",
"type": "object",
"properties": {
"legal-code": {
"type": "string",
"description": "The legal code of the rental license."
},
"human-readable": {
"type": "string",
"description": "The human readable rental license deed."
},
"machine-readable": {
"type": "string",
"description": "The machine readable code of the rental license that can be recognized by software."
}
}
}
```
<br>
The rental license metadata SHOULD be stored on a decentralized storage service such as IPFS, adopting an IPFS-style URI that encodes the hash of the metadata for integrity verification.
The full API available with this standard, **ERC-4907**, and **ERC-5218** is shown in the diagram below (excluding **ERC-721**):
<br>
<p align="center">
<img src="diagrams/eip-9999.png" width="80%" align="center"/>
<br>
<br>
## Backward Compatibility
This standard is compatible with the current **ERC-721**, **ERC-4907**, and **ERC-5218** standards. A contract can inherit from **ERC-9999**, **ERC-721**, **ERC-4907**, and **ERC-5218** at the same time.
In addition, functions introduced in **EIP-9999** have many similarities with the existing functions from these standards, allowing developers to adopt the standard easily.
<br>
## Reference Implementation
A simple implementation of this standard, illustrating the data structures for `UserRentalInfo` (expanded from **ERC-4907**) and `RentalLicense` (compatible with **EIP-5218**) is shown below:
```solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.16;
import "forge-std/Test.sol";
import "./IERC9999.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
contract ERC9999 is IERC9999, Ownable {
struct UserRentalInfo {
address user;
uint64 expires;
uint256 licenseId;
}
struct RentalLicense {
uint256 tokenId;
uint256 parentLicenseId;
string uri;
}
mapping (uint256 => UserRentalInfo) private _users; // maps from an NFT to its user, expiration, and rental license identifier
mapping (uint256 => RentalLicense) private _licenses; // maps from a rental license identifier to a rental license struct object
mapping (uint256 => uint256) private _licenseIds; // maps from an NFT to its rental license identifier
/// @notice Get the rental license of an NFT.
/// @dev The zero value indicates that there is no license.
/// Throws if `tokenId` is not a valid NFT.
/// @param tokenId The NFT to get the rental license for.
/// @return licenseId The identifier of the rental license.
function userRentalLicense(uint256 tokenId) external view returns(uint256) {
require(tokenId != 0, "ERC9999: tokenId is not valid");
return _users[tokenId].licenseId;
}
/// @notice Set the temporary user, expires, and rental license of an NFT.
/// @dev The zero address indicates that the rental NFT has no user.
/// Throws if `tokenId` is not valid NFT.
/// Throws if `licenseId` is not a valid license.
/// Throws if `msg.sender` is not the owner of the NFT.
/// Throws if `expires` is in the past.
/// @param tokenId The NFT to be rented.
/// @param user The new user of the NFT.
/// @param licenseId The identifier of the rental license.
/// @param expires UNIX timestamp the new user can use the NFT before it expires.
function setUserRentalLicense(uint256 tokenId, address user, uint256 licenseId, uint64 expires) external onlyOwner {
require(tokenId != 0, "ERC9999: tokenId is not valid");
require(licenseId != 0, "ERC9999: licenseId is not valid");
require(expires > block.timestamp, "ERC9999: expires is not valid");
_users[tokenId] = UserRentalInfo(user, expires, licenseId);
emit UpdateRentalLicense(tokenId, licenseId, user, expires);
}
/// @notice Create a new rental license.
/// @dev The zero value for parentLicenseId indicates the license has no parent.
/// Throws if `tokenId` is not valid NFT.
/// Throws if `msg.sender` is not the owner of the NFT.
/// Throws uri is invalid.
/// @param tokenId The NFT the rental license is issued upon.
/// @param parentLicenseId The identifier for the parent license.
/// @param uri The URI of the license terms.
/// @return licenseId The identifier of the created rental license.
function createRentalLicense(uint256 tokenId, uint256 parentLicenseId, string memory uri) external onlyOwner returns (uint256) {
require(tokenId != 0, "ERC9999: tokenId is not valid");
require(bytes(uri).length != 0, "ERC9999: uri is not valid");
uint256 licenseId = _getNextLicenseId();
_licenses[licenseId] = RentalLicense(tokenId, parentLicenseId, uri);
_licenseIds[tokenId] = licenseId;
emit CreateRentalLicense(licenseId, tokenId, parentLicenseId, uri);
return licenseId;
}
/// @notice Get the next rental license identifier.
/// @dev Throws if the identifier overflows.
/// @return licenseId The next rental license identifier.
function _getNextLicenseId() private view returns (uint256) {
uint256 _licenseIdTracker = 1;
while (true) {
if (_licenseIds[_licenseIdTracker] == 0) {
break;
}
_licenseIdTracker++;
}
require(_licenseIdTracker < 2**256 - 1, "ERC9999: licenseId overflow");
return _licenseIdTracker;
}
}
```
<br>
## Test Cases
Test cases are available [here](eip-9999-test/):
```solidity
pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol";
import {ERC9999} from "../src/erc9999.sol";
contract ERC9999Test is Test {
ERC9999 e;
address user = address(0xbeef);
string tokenURI = "someTokenURI";
string licenseURI = "someLicenseURI";
uint256 tokenId = 1;
uint256 parentLicenseId = 0;
uint64 expires = 1737586800;
event CreateRentalLicense(uint256 licenseId, uint256 tokenId, uint256 parentLicenseId, string uri);
event UpdateRentalLicense(uint256 tokenId, uint256 licenseId, address user, uint64 expires);
function setUp() public {
vm.deal(user, 10 ether);
e = new ERC9999();
}
function testERC9999() public {
// Test license creation (createRentalLicense)
vm.expectEmit();
emit CreateRentalLicense(1, tokenId, parentLicenseId, licenseURI);
uint256 licenseId = e.createRentalLicense(tokenId, parentLicenseId, licenseURI);
assertEq(e.getLicenseURI(licenseId), licenseURI, "License URI should match");
// Test setting userRentalLicense
vm.expectEmit();
emit UpdateRentalLicense(tokenId, licenseId, user, expires);
e.setUserRentalLicense(tokenId, user, licenseId, expires);
// Test license lookup (userRentalLicense)
assertEq(e.userRentalLicense(tokenId), licenseId, "LicenseId should match");
}
}
```
<br>
## Security Considerations
This EIP standard can inherently protect the owner's rights as they can change the NFT user and `expires` at any time.
Implementation of the **IERC9999** standard MUST consider following the security considerations from **IERC5218** if they are to implement their full license standard.
<br>
## Copyright
Copyright and related rights waived via [CC0](LICENSE.md).

View File

@ -0,0 +1,196 @@
## design of a protocol for management and bartering of game (NFT) assets
<br>
### I. introduction
<br>
we leverage the principles of [simplicity first](http://principles-wiki.net/principles:gall_s_law), [composability](https://a16zcrypto.com/posts/article/composability-is-to-software-as-compounding-interest-is-to-finance/), and extensibility to design a plan to implement an end-to-end (first version of a) marketplace protocol for managing game NFT assets.
<br>
<p align="center">
<img src="https://github.com/go-outside-labs/blockchain-infrastructure-design/assets/138340846/74456b31-d8c1-4462-8fb8-d303c51a3cd5" width="80%" align="center"/>
<br>
<br>
our protocol is designed to take advantage of non-fungible tokens (*i.e.*, EVMs compatible standards such as [ERC-721](https://ethereum.org/en/developers/docs/standards/tokens/erc-721), [ERC-1155](https://ethereum.org/en/developers/docs/standards/tokens/erc-1155), and extensions) for game assets, which includes **ownership** (the address that holds the token or the), provable [scarcity](https://vitalik.eth.limo/general/2021/03/23/legitimacy.html) (through rarity traits and authenticity), [interoperability](https://www.playtoearn.online/whats-interoperability/), and **emitting events** when the state changes.
storage and computation costs are expensive and restricted on the ethereum blockchain. to overcome the challenges regarding **searching, sorting, notification**, and **bartering**, our **hybrid design contains both on-chain and off-chain components**.
in addition, the [blockchain trilemma](https://vitalik.eth.limo/general/2021/04/07/sharding.html) states that only two can be guaranteed among: decentralization, security, and scalability (similarly to the broader [CAP theorem](https://en.wikipedia.org/wiki/CAP_theorem)). since ethereum communities emphasize that [decentralization](https://onezero.medium.com/why-decentralization-matters-5e3f79f7638e) and security are prerogatives, we design a solution that improves scalability through an **off-chain microservice infrastructure** (which can be deployed in a cloud service such as [AWS](https://aws.amazon.com/), [GCP](https://cloud.google.com/?hl=en), [azure](https://azure.microsoft.com/en-us/), [vercel](https://vercel.com/), etc.).
while these choices might initially compromise certain aspects of decentralization of the protocol, our **modular design** allows for gradual improvements and [progressive decentralization](https://a16zcrypto.com/posts/article/progressive-decentralization-crypto-product-management/) in future versions that could take full advantage of blockchain technologies, for instance, by exploring layer-2 strategies.
In the subsequent sessions, we discuss the design and roadmap for our protocol, concluding with a survey on improvements, roads not taken, and their merits and demerits.
<br>
----
<br>
### II. assumptions
<br>
we make the following assumptions in our design:
<br>
* the **game's assets smart contracts are already available in another protocol and platform**, which we have access to through the blockchain APIs.
<br>
* the player already has a **digital wallet on which the assets are minted to**. the assets are not [soulbound](https://vitalik.ca/general/2022/01/26/soulbound.html), *i.e.,* they can be transferable.
<br>
* the game's assets smart contracts contain (immutable) **metadata that indicates an asset's rarity and utility** (*e.g.,* name, category, permanent URL for thumbnail, traits, required level).
- our protocol will use this data to calculate a "rating" for the asset, which facilitates bartering (illustrated in the frontend sketch as a 1-5 stars rating).
- the algorithm calculating this rating is out of the scope of this document, but we incorporated it into our modular microservice infrastructure.
<br>
* although this is a high-level design and we won't explicitly specify our protocol's smart contracts, they should contain **two mutable `boolean` variables** (or a logic variation):
- `EQUIPPED`: specifying that the player's avatar is wearing the asset. this variable should be in sync with the game's main protocol.
- `FOR_TRADE`: specifying that the player has marked the asset for bartering.
<br>
* as our protocol only focuses on bartering, the original asset's smart contracts have **no logic for secondary market fee**s (or are irrelevant in our context).
<br>
* our protocol's smart contracts contain **logic for a marketplace's fee in each barter transaction**, and this is the only fee we consider in this design.
<br>
----
<br>
### III. protocol layers
<br>
<br>
<p align="center">
<img src="https://github.com/go-outside-labs/blockchain-infrastructure-design/assets/138340846/7ece4870-3383-44a8-b9bc-4404d1327a5d" width="100%" align="center"/>
<br>
<br>
the **frontend layer** should provide the functions sketched in figure 1, consisting of a simple dashboard that can be built on a **javascript/typescript framework**:
* the only extra complexity is the **integration with browser wallets**.
* **signning up or logging in should leverage the game's original protocol** or simply be **achieved through the user's wallet**, as we are not creating a separate profile database or gathering personal data for the user.
<br>
the **backend layer** consists of:
* a **database solution to track bartering operations** (*e.g.*, a noSQL such as [mongoDB](https://www.mongodb.com/) or an RDBMS supporting big data).
- in the simplest form, every time a user places a bartering bid on an asset, the database could create an entry with the asset's and the user's `address` (or `tokenId`). this entry would be deleted when the bid is refused or accepted.
- this approach could be later replaced by decentralized alternatives (*e.g.,* [kwil](https://www.kwil.com/), [orbitDB](https://orbitdb.org/), [bigchainDB](https://www.bigchaindb.com/), [convenantSQL](https://developers.covenantsql.io/docs/en/intro), [ceramic](https://ceramic.network/)).
* an **orchestration infrastructure**, through a [kubernetes](https://kubernetes.io/) solution written on [terraform](https://www.terraform.io/).
* **microservices** for off-chain **searching and sorting** (*e.g.*, [ELK](https://aws.amazon.com/what-is/elk-stack/), [biguery](https://cloud.google.com/bigquery/), [cloudsearch](https://aws.amazon.com/cloudsearch/), [hive](https://hive.apache.org/)), **notification system** (*e.g.*, a simple cloud messaging service such as [AWS SNS](https://aws.amazon.com/sns/) or a more elaborated pub/sub solution such as [AWS SQS](https://aws.amazon.com/sqs/), [Kafka](https://kafka.apache.org/), [ZeroMQ](https://zeromq.org/), [AWS Kinesis](https://aws.amazon.com/kinesis/), [RabbitMQ](https://rabbitmq.com/)), and an **asset rating evaluation** (through an in-house algorithm leveraging the assets' traits as input).
* a **CI/CD pipeline** (*e.g.*, [jenkins](https://www.jenkins.io/), [circleci](https://circleci.com/), [gitHub actions](https://github.com/features/actions)).
* depending on the scale of the project, we might want to rely on something other than [blockchain data availability](https://ethereum.org/en/developers/docs/data-availability), so we could consider adding a **database to cache the smart contract events and keep track of ownership** (*e.g.*, [Redis](https://redis.io/)). This extra step is included in the roadmap below.
* likewise, any gateway and CDN solution should be incorporated in this layer.
<br>
finally, the **blockchain layer** contains our protocol smart contracts and their test suites. a multi-signature wallet (*e.g.,* [gnosis](https://www.gnosis.io/)) could be leveraged to enhance the security of this deployment.
<br>
<p align="center">
<img src="https://github.com/go-outside-labs/blockchain-infrastructure-design/assets/138340846/803d2e05-6e06-46c0-afd7-ef05c0010ece" width="100%" align="center"/>
<br>
<br>
----
<br>
### IV. implementation roadmap
<br>
<br>
<p align="center">
<img src="https://github.com/go-outside-labs/blockchain-infrastructure-design/assets/138340846/a857cacc-811e-43db-9b7b-eca99ff1c8a7" width="100%" align="center"/>
<br>
<br>
----
<br>
### V. roads not taken in our approach
<br>
#### a full on-chain narketplace for auctions with a native token
<br>
ideally, a marketplace should use smart contracts to fully control asset auctions and transactions. examples of this approach are [sandbox](https://www.sandbox.game/en/) or [axie infinite marketplace's contract for auction](https://etherscan.io/address/0xf4985070ce32b6b1994329df787d1acc9a2dd9).
however, since we are only focusing on bartering, this solution is overkill as creating a new token brings several challenges, such as extra engineering resources, adoption, tokenomics design, and security concerns.
<br>
#### a full on-chain decentralized autonomous bartering algorithm
<br>
in general, finding [multiple "coincidence of wants"](https://www.geeksforgeeks.org/what-is-barter-system-and-double-coincidence-of-wants/) of various assets translates to finding hypercycles in directed hypergraphs (an [NP-complete](https://en.wikipedia.org/wiki/NP-completeness) problem). Therefore, the bartering problem in a distributed setting is similar to [dijkstras bankers problem](https://en.wikipedia.org/wiki/Banker's_algorithm).
<br>
<p align="center">
<img src="https://github.com/go-outside-labs/blockchain-infrastructure-design/assets/138340846/5df0628e-8fef-4625-8e9f-e83a878fad81" width="40%" align="center"/>
<br>
<br>
an on-chain implementation of this algorithm is tricky. In its most naive form, the design could be costly and probably prone to tampering, fraud, and MEV exploitation. in addition, it would need to assume a large amount of simultaneous trades.
<br>
#### a coincidence-of-wants bartering algorithm with off-chain batches
instead of leveraging an off-chain database solution, we could move our bartering logic on-chain by implementing a system for "coincidence of wants" with batch transfers.
in this approach, when multiple [intents](https://www.paradigm.xyz/2023/06/intents) for the same asset are within a batch, there could be an opportunity for a peer-to-peer swap that doesn't rely on the main network.
a successful example in DeFi is the [cowswap](https://cow.fi/) protocol, which collects and aggregates intents off-chain, setting them on batches. the batches are run by third-party solvers.
this design would need to assume a large number of trades at the same time and that there will be willed solvers (or an extra in-house structure) to run the batches.
<br>
#### an NFT-focused chain or L2
a few successful decentralized game platforms (such as [enjin](https://enjin.io/) and [immutable](https://www.immutable.com/)) have adopted an NFT-focused chain or L2 solution to customize the process of multiple NFT transfers, tracking metadata and provenance, facilitating intellectual property, and interoperability among chains.
however, there is a massive overhead to building, maintaining, governing, and creating the adoption of a new blockchain and a native token, making this solution unsuitable for our bartering protocol.

View File

@ -8,7 +8,7 @@
![blockchain intel - mvp](https://user-images.githubusercontent.com/1130416/224561560-3fd67632-fba6-497c-b3b6-c5c5298701f0.png)
##### 📚 more details can be found in my mirror post, **[quant #3: building a scalable event scanner for ethereum](https://mirror.xyz/steinkirch.eth/vSF18xcLyfXLIWwxjreRa3I_XskwgnjSc6pScegNJWI)**.
##### 📚 more details can be found in my mirror post, **[quant #3: building a scalable event scanner for ethereum](https://mirror.xyz/go-outside.eth/vSF18xcLyfXLIWwxjreRa3I_XskwgnjSc6pScegNJWI)**.
<br>
@ -83,7 +83,7 @@ optional arguments:
<br>
follow [this instructions](https://mirror.xyz/steinkirch.eth/vSF18xcLyfXLIWwxjreRa3I_XskwgnjSc6pScegNJWI) to deploy the indexer to [vercel](https://vercel.com/) and [mongodb atlas](https://cloud.mongodb.com/v2/640ec23b5c46a564602b7c0e#/overview).
follow [this instructions](https://mirror.xyz/go-outside.eth/vSF18xcLyfXLIWwxjreRa3I_XskwgnjSc6pScegNJWI) to deploy the indexer to [vercel](https://vercel.com/) and [mongodb atlas](https://cloud.mongodb.com/v2/640ec23b5c46a564602b7c0e#/overview).
<br>

View File

@ -8,7 +8,7 @@ setup(
'src.blockchains',
'src.server',
'src.utils']),
author="steinkirch.eth",
author="mia stein,
install_requires=['python-dotenv'],
entry_points={
'console_scripts': ['indexer=src.main:run']