It seems extremely likely that NFTs (non-fungible tokens) will become ubiquitous in many aspects of our digital (and physical) world in the mid to long-term future. As NFTs become more widely adopted, multiple parties may wish to share ownership of a single NFT. Dividing the ownership of NFTs is possible on smart programming platforms by locking the NFT in a contract in return for a specified quantity of tokens that each represent partial ownership of the locked NFT. These tokens may then be distributed to multiple accounts. This process is known as "fractionalization" of an NFT. The aim of this project is to implement a simple EVM-based dapp (decentralized application) that allows a user to fractionalize their ERC721 NFTs into ERC20 tokens. The most popular example of a dapp that implements this functionality is fractional.art.
Actions:
- Fractionalize: An NFT owner can fractionalize their NFT by transferring the NFT to the contract. In return the NFT owner receives the entire supply of a newly created ERC20 token that they specified at fractionalization. The NFT Owner also sets an initial buyout price for the NFT at fractionalization.
- Buyout: A buyer can buyout (purchase) the locked NFT by transferring (at least) the specified amount of ETH to the contract. The NFT gets transferred to the buyer's account.
- Claim: Following a buyout, an ERC20 token holder of a fractionalized NFT can claim their share of the buyout price received from the buyer as determined by the proportion of the ERC20 token supply that they hold. This is also referred to as a payout in the frontend.
- Redeem The holder of all ERC20 tokens can redeem the NFT by sending all of the the ERC20 tokens to the contract; the NFT is transferred to the redeeming account. Redemption allows the holder of all the tokens to "unfractionalize" the NFT. The most practical use case is that the original owner decided they no longer wanted to fractionalize the NFT.
As a side effect, the above functionality also allows the dapp to act as a simple marketplace (or escrow) for accounts to sell NFTs in exchange for ether.
Link to Public Interface: frac.netlify.app
Screencast on YouTube
(also checked in at ./FractionalizeNFTScreencast.mp4.
.github/workflows
: For github actions (currently the contracts are compiled and unit-tested automatically).client
: Frontend for the FractionalizeNFT contract.docs
: Additional documentation.contracts
: FractionalizeNFT and helper smart contracts.tests
: Python unit tests (executed via brownie).
- Node.js >= v14.
- gananche-cli.
- Python >= 3.6 (during development mainly 3.8 and 3.9 were used).
- brownie and the Python package dependencies given in requirements.txt.
python3 -m venv venv # Create a local virtual environment to install packages in.
source venv/bin/activate # Bash, activate scripts for shells available in same folder.
pip install --upgrade pip
pip install -r requirements.txt
The contracts are compiled and tested upon push in Github CI, the results can be seen under Github Actions.
To run the tests locally run brownie test
(this will start ganache-cli for you):
source venv/bin/activate # If virtual environment not already activated
brownie test
Or, with gas profiling and coverage enabled (slower):
brownie test --gas --coverage
The scripts/deploy.py
script can be used to deploy the contracts locally and populate the FractionalizeNFT contract with some NFTs
ready for manual testing:
- Start the console:
source venv/bin/activate # If virtual environment not already activated
brownie console
- From within the console, run:
nft_contract, frac_contract = run("deploy", "main_dev")
A mnemonic may be added to .env
in the form
MNEMONIC=YOUR MNEMONIC ... PHRASE HERE
This mnemonic value will be used in the brownie config and applied when starting ganache-cli.
Note: See "Deploying for Manual Testing" for how to deploy the contract and populate it with fractionalized NFTs ready for use with the frontend.
- Run:
cd client yarn install yarn start
- Open http://localhost:3000.
Deploy to Ropsten (and verify source code via Etherscan):
brownie run deploy.py --network ropsten
Source code verification additionally requires:
- An Etherscan API token
.env
entry in the form:ETHERSCAN_TOKEN=<TOKEN>
- Brownie >= 1.17.0.
0x7b13c2F7AaA8C772Bd0a13A86B0CF633fAf790B0
(danceratopz.eth)
Solidity:
- Feature: Allow an ERC20 token holder to update the buyout price weighted by the proportion of the ERC20 token supply that they hold.
- Feature: Delete an entry from
fracNFTs
if it is no longer in use, i.e., when:- An NFT has been redeemed, or
- An NFT has been bought and there are no pending payouts (the contract holds the entire supply of the corresponding ERC20 token).
- Chore: Improve unit test coverage.
- Chore: Implement as an upgradable contract.
Frontend:
- Refactor: Remove inline styling (to CSS).
- Feature: Allow a user to open a detailed view of a fractionalized NFT to display its full and account-specific information (e.g., percentage of ERC20 tokens held).
- Feature: Add a pager for the Listings on the Market, Redeem and Payout.
- Feature: Render Listings on the Market, Redeem and Payout pages asynchronously.
- Fix: Improve/optimise image loading.
- Feature: Make use of emitted events from the smart contract.
- Feature/Clean-up: Remove the Redeem page. It can be incorporated in the Market, or the potential "Detail" view as a "Redeem" or "Cancel" button.
- Chore: Add unit tests.
- Chore: Upgrade packages.