In this project, we developed two smart contracts based on ERC721 token.
First contract is LegalEntityVerification.sol for State.
This contract has the following modifiers:
onlyStateAuthority(address _address)
: Allows only state authority to perform actions.onlyNonVerified(address _address)
: Allows only non-verified address to be performed on.onlyVerified(address _address)
: Allows only verified address to be performed on.
This contract has the following fields:
mapping(address=>bool) verifiedAddresses
: This mapping returns true or false based on if given address is verified or not.address public stateAuthority
: Address of the state, it's public since all manufacturers have to know and access that.
This contract has the following functions:
verify(address _address)
: Avoid
function. Ifmsg.sender
is the state address and given_address
is not verified before, maps_address
astrue
inverifiedAddresses
.unverify(address _address)
: Avoid
funciton. Ifmsg.sender
is the state address and given_address
is verified before, maps_address
asfalse
inverifiedAddresses
.isVerified(address _address)
: If_address
is verified before, returnstrue
. Else, returnsfalse
.
Second contract is Provenance.sol.
This contract has the following modifers:
onlyVerifiedAddress(address _address)
: Only addresses that are verified by the state authority(see LegalEntityVerification.sol) can perform actions.onlyExistentToken(uint256 _tokenId)
: Only tokens that are in circulation(or minted we can say) can be subject to actions.onlyNonApprovedToken(uint256 _tokenId)
: Only tokens that are in the approval state can be subject to actions.onlyAuthorized(address _address)
: Only authorized(in this case thefactory
) can perform actions.onlyOwner(uint256 _tokenId,address _address)
:Only the owner of the token can perform actions.onlyApproved(uint256 _tokenId,address _address)
:Only approved tokens can be subject to actions.
This contract has the following fields:
uint256 public serialId
: Serial Product id. Starts with 0. Increments by one in every mint.address public factory
: Address of the factory.address public legalEntityContractAddress
: Address of state contract.mapping(uint256=>address[]) owners
: holds information of all owners' addresses in order. First owner's address isowners[0]
.mapping(uint256=>Product) products
: Holds the information about the product. When we mint a token, we are mapping an instance of theProduct
struct to have more information about the product.mapping(uint256=>bool) approvalState;
: Holds the information about the approval state of the queried token.
This contract has the following functions:
mintProductToken(uint256 _zipCode)
: If address is authorized by state, mints the token by using _safeMint(msg.sender,productId) function of ERC721. Adds the factory address to the owners, and increments serialID by one.getTheOriginAddress(uint256 _tokenId)
: Returns the first owner of that token.approveOwnership(uint256 _tokenId)
: When we transfer a token, maybe the receiver might not be aware that a token is sended to her. In depth, she might not even want to own the token, so we have to provide a solution for the receiver that she will approve or not approve the ownership of the token whenever she is aware of the transaction.transferToken(address _from,address _to, uint256 _tokenId)
: Transfers the token which has giventokenId
by using_transfer(_from,_to,_tokenId)
function of ERC721.
There are several getters for easier access to fields or mapped values in both functions. We are not going to crowd this file with them since they are self-explaining and the comments in the code will clear any confusions.
A simple flow happens as follow:
- The state contract is initiated.
- Provenance contracts are initiated with the address of the state contract deployed.
- Tokens are minted by
mintProductToken
function. - Tokens can be transferred by
transferToken
function. - If receiver accepts the transferred token, she approves the ownership by calling the
approveOwnership
function and she becomes the new owner of the token. Else, the token's ownership history will not show the latest address who received the token.
We used Truffle in this project for testing and deploying to Rinkeby
and Fuji
.
First install the required dependencies by running:
npm install
For running the tests:
truffle test
For deploying to any network(listed in the truffle-config.js
file):
- You have to first specify the network you want to deploy and configure the
truffle-config.js
file for your needs. We have used primary network for testing inRinkeby
, using Infura as provider. - This project is tested it's migrations process in
Avalanche Fuji Testnet
andRinkeby
. - In order to run migrations, you have to configure an
.env
file in the directory, same directory thattruffle-config.js
is located. - Here is the boilerplate:
INFURA_RINKEBY_URL = <YOUR_INFURA_URL>
AVALANCHE_FUJI_TESTNET_URL = https://api.avax-test.network/ext/bc/C/rpc
ETHERSCAN_API_KEY = <YOUR_ETHERSCAN_API_KEY>(Not necessary if you only want to migrate)
MNEMONIC = <YOUR MNEMONIC>
- After your configuration, you can migrate the project with the following command to any network.
truffle migrate --network <NETWORK NAME> [--reset(if you have deployed the contracts before, it's cleaner, else no need for this flag.)]
Contract addresses may vary for each deployment. However, you can follow the latest deployment on account in here.
- We have used React as our frontend library. You can see the source code under here.
- You should have migrated your contracts before interacting with the interface. Please see usage section above.
- Open a terminal in the
client
directory. - Run:
npm install
- For starting in your local computer, run:
npm start
- And you are set, ready to interact with the app.
- You can see Legal Entity address on top of the screen. You can see your account's address, verification status owned token ids and tokens awaiting to be approved.
- You should enter the receiver's address on the left box and token id to the right box. Then, that token will disappear from the owned token section when you click "Send Token!".
- When you enter a token id that awaits approval to the Approve Token box and click "Approve Ownership!", that token will disappear from the awaiting approval section and appear on the owned token section if the operation succeed.
- When you enter a token id and click "Get the history of the token!", origin of the token will be shown on the top of the result and current owner will be shown on the bottom. The owners in between origin and current user shown in order.
- We have verify and unverify operations in addition to Provenance User Interface operations and fields. Also, you can mint a token if you are the provenance chairman. Mint operation takes a few seconds to be completed, and sends a completion message to the user.
- How to ensure there is only one state? : We pass the address of the deployed state authority to the Provenance constructor, meaning that only state we will listen is the one with given address.
- How to ensure no token has two owners at the same time? : ERC721 handles it.
- Doğukan Türksoy @ooodogodogodogo
- Deniz Sürmeli @denizvsurmeli