StarNotary smart contract allows for the ownership and transfer of stars, that is deployed on the Rinkeby public test network. The star notary contract is inherited from ERC721 , that is a minimum interface smart contract that allows for unique tokens to be managed, owned and traded.
List of contract functions:
Default functions that came with starter code
createStar()
putStarUpForSale()
make_payable()
buyStar()
Newly created functions
lookUptokenIdToStarInfo()
exchangeStars()
transferStar()
Contracts tokens are named "StarToken" with a symbol of "SRT".
string public constant name = "StarToken";
string public constant symbol = "SRT";
Allows looking up the star name by id. By indexing a mapping variable called tokenIdToStarInfo the name of the star is returned as a string.
// Implement Task 1 lookUptokenIdToStarInfo
function lookUptokenIdToStarInfo (uint _tokenId) public view returns (string memory) {
//1. You should return the Star saved in tokenIdToStarInfo mapping
return tokenIdToStarInfo[_tokenId].name;
}
Allows for two star to be exchanged. Two star ID's are inputs and through the use of "ownerOf" the address owner can be known. By comparing the sender address (one making the request) to the ownerOf ID address we can identify the sender is the owner of one of the stars. This prevents any two stars from being traded. The transferFrom function allows the transfer of stars. This function is within the ERC721.
// Implement Task 1 Exchange Stars function
function exchangeStars(uint256 _tokenId1, uint256 _tokenId2) public {
//1. Passing to star tokenId you will need to check if the owner of _tokenId1 or _tokenId2 is the sender
address sender = msg.sender;
//2. You don't have to check for the price of the token (star)
//3. Get the owner of the two tokens (ownerOf(_tokenId1), ownerOf(_tokenId1)
address address1 = ownerOf(_tokenId1);
address address2 = ownerOf(_tokenId2);
//4. Use _transferFrom function to exchange the tokens.
if (sender == address1){
//token 1 is owned by sender
_transferFrom(address1, address2, _tokenId1);
_transferFrom(address2, address1, _tokenId2);
}else if (sender == address2){
// token 2 is owned by sender
_transferFrom(address2, address1, _tokenId2);
_transferFrom(address1, address2, _tokenId1);
}
}
Allows for the transfer of a star from one address to another. Receivers address and senders star ID are inputs. Checks if sender address owns the star, if true the star is transferred with a ERC721 function.
// Implement Task 1 Transfer Stars
function transferStar(address _to1, uint256 _tokenId) public {
//1. Check if the sender is the ownerOf(_tokenId)
address address1 = ownerOf(_tokenId);
//2. Use the transferFrom(from, to, tokenId); function to transfer the Star
if(msg.sender == address1){
_transferFrom(address1, _to1, _tokenId);
}
}
A instance of the contract is deployed in each test statement. Stars are also created depending on the type of test. Stars are owned by address given by truffle.
Testing token name and symbol is done by calling out public variables within the contract and checked with conditional statements "assert.equal()". Star name is also tested by creating a star with a name "Mystar", then testing that newly created star name.
it('can add the star name and star symbol properly', async() => {
//1. Create a star
let instance = await StarNotary.deployed();
let user1 = accounts[1];
let starId = 6;
await instance.createStar('Mystar', starId, {from: user1});
//2. Call the name and symbol properties in your Smart Contract and compare with the name and symbol provided
let ContractName = await instance.name(); //get Token name
let ContractSymbol = await instance.symbol(); // get token symbol
let starLookUp = await instance.lookUptokenIdToStarInfo(starId); //get star name
assert.equal(ContractName,'StarToken');
assert.equal(ContractSymbol,'SRT');
assert.equal(starLookUp,'Mystar');
});
Testing exchange stars requires two address both with owned stars for the exchange. Then passing the starId's to the exchangeStars function within the contract. To check if the exchange worked, the ownership of the stars will be in the other address.
This table my help with accounts to stars ownership.
Alice | Bob |
---|---|
StarID: 7 | StarID: 8 |
StarName TheMstar | StarName TheGstar |
it('lets 2 users exchange stars', async() => {
let instance = await StarNotary.deployed();
// two address accounts
let Alice = accounts[0];
let Bob = accounts[1];
// two stars with IDs
let starId = 7;
let starId2 = 8;
// 1. Create 2 Stars
await instance.createStar('TheMstar',starId, {from: Alice}); // Alice owns star ID 7
await instance.createStar('TheGstar',starId2, {from: Bob}); // Bob owns star ID 8
// 2. Call the exchangeStars functions implemented in the Smart Contract
await instance.exchangeStars(starId,starId2);
let star1 = await instance.ownerOf.call(starId); // get new owner of star ID 7
let star2 = await instance.ownerOf.call(starId2); // get new owner of star ID 8
// 3. Verify that the owners changed
assert.equal(star1,Bob); // Bob should own star ID 7
assert.equal(star2,Alice); // Alice should own star ID 8
});
Testing transfer stars needs two address with a single star in only one address. After calling transferStar with the receivers address and senders star ID as inputs. Then by confirming that the receivers address is now the new owner of the star, the test will pass.
it('lets a user transfer a star', async() => {
let instance = await StarNotary.deployed();
let Alice = accounts[0];
let Bob = accounts[1];
let starId = 9;
// 1. create a Star
await instance.createStar('TheMstar',starId, {from: Alice}); // create the star in Alice's account
// 2. use the transferStar function implemented in the Smart Contract
await instance.transferStar(Bob,starId); // Transfer Alices star to Bobs account by star ID 9
// 3. Verify the star owner changed.
let stars = await instance.ownerOf.call(starId); // get owner of star ID 9
assert.equal(stars,Bob); // Bob should own star ID 9
});
functions:
default functions setStatus: changes the html text tags by ID
createStar: allows a user to create a star on the blockchain with a metamask address
Newly created functions
lookUp: allows a user to lookup a star name by ID through the use of a contract function.
setStatus: function(message,htmlID) {
const status = document.getElementById(htmlID);
status.innerHTML = message;
},
// Implement Task 4 Modify the front end of the DAPP
lookUp: async function (){
let { lookUptokenIdToStarInfo } = this.meta.methods;
let { symbol } = this.meta.methods;
let { name } = this.meta.methods;
let id = document.getElementById("lookid").value;
id = parseInt(id);
let starName = await lookUptokenIdToStarInfo(id).call(); // call lookUptokenIdToStarInfo function within the contract
let contract = await name().call();
let sym = await symbol().call();
if (starName.length == 0){ // if starName is zero then no name exist and therefor not owned
App.setStatus("Star not owned.","status");
App.setStatus("Star ID: ","starData");
App.setStatus("Token Name: ","contract");
App.setStatus("Token Symbol: ","symbol");
}else{ // else its owned and displayed by passing tag ID to setStatus
App.setStatus("Star owned.","status");
App.setStatus("Star ID: "+id+" is named "+starName,"starData");
App.setStatus("Token Name: "+contract,"contract");
App.setStatus("Token Symbol: "+sym,"symbol");
}
}
My Versions
Token address c963a21eb4d6445bbac51a143477b898
openzeppelin-solidity v2.1.2
nodejs v11.3.0
Truffle v5.0.3
webpack (web3) 4.28.1
clone the git repository
git clone https://github.com/MitchTODO/Private-Blockchain.git
cd into repository
Install truffle
npm -g install truffle
Run truffle develop in the project repo:
truffle develop
You should see truffle running on http://127.0.0.1:9545/ with a development console
Next compiling the contract, inside the development console, run:
compile
Then migrating the contract to the locally running Ethereum network, inside the development console, run:
migrate --reset
Testing the StarNotary contract with TestStarNotary.js inside the development console, run:
test
Note you will need web3 for the Front End of the DAPP, this should automatically install with truffle. But if you have problems with webpack-dev-server. Find that module in app/node_modules/webpack-dev-server and delete it.
Then run
npm install webpack-dev-server
Open another terminal window and go inside the project directory, and run:
cd app
npm run dev
Make sure you have metaMask chrome extension install
Once installed click on the little fox top left of browser and then on the drop down for different networks. Select Custom RPC, scroll down to new network and paste http://127.0.0.1:9545 then save.
If successful you should see you are connect to the local network.
Now we need to import one of are truffle address so we can create some stars.
Go to the truffle development console and copy a private key. This key will be used to import the address into metaMask. Go back to metamask and click on the circle of colors then import account. Note Make sure your still on the local network. Paste your private key then select import. If done right you should see your new test account with 100 ETH. Make sure you have this account selected.
To view the DAPP point your browser to http://127.0.0.1:8080
Next create a star with a star name (string) and star ID (integer), then press create star. You should see a metaMask notification allowing you to confirm a contract interaction.
Looking up a star by id.
Open the package-lock.json and verify that truffle-hdwallet-provider and openzeppelin-solidity dependencies are installed. If not you can always install it with the commands:
npm install --save truffle-hdwallet-provider
npm install --save openzeppelin-solidity
Then opening truffle-config.js file to allow deployment to rinkeby.
Edit networks like so:
Note you will need your metaMask seed words found in settings within metaMask. Also a infura contract address, the one below is already in used. Use infura to create a new contract address on rinkeby.
networks: {
// Useful for testing. The `development` name is special - truffle uses it by default
// if it's defined here and no other network is specified at the command line.
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
// tab if you use this network and you must also set the `host`, `port` and `network_id`
// options below to some value.
//
development: {
host: "localhost", // Localhost (default: none)
port: 9545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
},
rinkeby: {
provider: function() {
return new HDWalletProvider("<YOUR SEED WORDS>","https://rinkeby.infura.io/v3/cdf64f4c7d234ad199892d66ae7a4c6c")
},
network_id: '4',
gas: 4500000,
gasPrice: 10000000000,
}
},
}
Deploying to Rinkeby, open a new console in the project repo and run:
truffle migrate --network rinkeby --reset
In another console run web3, run:
cd app
npm run dev
Note Rinkeby faucet must be used to get test ether to make transactions.
To view the DAPP point your browser to http://127.0.0.1:8080, make sure you selected a Rinkeby account in metaMask with Ether. Next create a star with a star name (string) and star ID (integer), then press create star. You should see a metaMask notification allowing you to confirm a contract interaction.
Contract will pend for a couple of seconds. Then you should see confirmation. Next look up the star you just created by entering the star ID.
Now we will view the new star Token on Etherscan
StarNotary smart contract is now deployed on Rinkeby test network.