Personal Notes:
USEFUL COMMANDS AND INFO:
- https://github.com/ethersphere/swarm
- decentralized web server
check balance: web3.fromWei(web3.eth.getBalance(web3.eth.accounts[1]), "ether").toNumber()
get instance of contract: ChainList.deployed().then(function(instance){app=instance})
unlock account: personal.unlockAccount(eth.coinbase, "INSERT PASSWORD", 600)
TRUFFLE: AFTER CHANGING CONTRACT, MUST RUN TRUFFLE MIGRATE --RESET
Test Network: 1) sh startganache-cli.sh 2) truffle migrate 3) npm run dev
PRIVATE NETWORK 1) in private folder, run sh startnode.sh 2) geth attach (if you are gonna use it) 3) truffle migrate 4) npm run dev
Setup:
-
truffle unbox pet-shop
-
fix truffle.js
-
create contract in contracts folder
ChainList.sol
-
create migration scripts in 2_deploy_contracts.js
-
truffle migrate
-
Interact with contract
truffle console
ChainList.address
web3.fromWei(eth.getBalance(eth.accounts[0]), "ether") <- account zero pays for launching contract
ChainList.deployed().then(function(instance){app=instance}) <- instance of contract
app.getObject.call()
app.sellObject("iPhone 7", "Selling to buy iPhone 8", web3.toWei(3, "ether"), {from: web3.eth.accounts[1]})
app.getObject.call()
Test Project:
Can Test with:
- ganache_cli
- private network
- test network
Test that state variables are initiated
- create and complete file
- ChainListHappyPath.js
- run test file
truffle test
-
Can dictate which specific file to test by running
truffle test test/ChainListHappyPath.js
Front End:
-
npm install (while in chainlist directory)
-
run current application
npm run dev
-
change package.json to whatever you want
-
index.html
-
app.js
- replace init function
-
create stylesheet
app.css
Connect Front End with Back End:
-
disable metamask extension so it doens't get in way
-
instantiate web3
- remove all functions in app.js
- leave the $(function()) at end
-
complete code
- remember that web3 uses callbacks and truffle uses promises
-
redeploy contract
truffle migrate --reset
- remove all functions in app.js
Sell article from front end:
-
remove previous constructor that created fake article
-
app.js
Sell article from MetaMask:
-
start-up node (differently than normal)
- starting ganache-cli with 3 pre-specified accounts with private key (account="") and value
- do this everytime so that we can use same accounts (already imported in MetaMask) as we use our app
- run sh startganache-cli.sh or below
ganache-cli --account="0x351494a5ae8f9b70a2a2fd482146ab4578f61d4d796685c597ec6683635a940e, 100000000000000000000" --account="0x4cd491f96e6623edb52719a8d4d1110a87d8d83e3fa86f8e14007cb3831c0a2b, 100000000000000000000" --account="0xef40e0d6ada046010b6965d73603cabae1a119ca804f5d9e9a9ce866b0bea7d, 100000000000000000000"
-
truffle migrate
-
npm run dev
-
re-enable MetaMask
-
connect MetaMask to localhost 8545
-
import accounts created with ganache-cli
- now the account shown on ChainList is whichever account you have open in MetaMask
Notify Users when new object is for sale (without reloading page)
-
add event to ChainList for notifying when new article is for sale
event sellObjectEvent(address indexed _seller, string _name, uint256 _price);
- indexed modifier allows you to filter event occurences by values of seller argument
-
trigger event in sellObject function
sellObjectEvent(seller, name, price);
-
truffle migrate --reset <- must --reset after changing contract code
Watch event from Truffle console and test it
-
get instance of deployed contract
ChainList.deployed().then(function(instance){app=instance;})
-
create event watcher
var sellEvent = app.sellObjectEvent({}, {fromBlock: 0, toBlock: 'latest'}).watch(function(error, event) {console.log(event);})
- the {} in sellObjectEvent is filter (could filter by seller because it is indexed)
-
sell object
app.sellObject("Object 1", "Description of object 1", web3.toWei(11, "ether"), {from: web3.eth.accounts[1]})
-
stop watching
sellEvent.stopWatching()
-
update test file
-
truffle test
Automatically update frontend with new object when event is triggered
- update app.js to listen to event
- don't forget to add App.listenToEvents() to initContract
- (MAYBE NOT) remove App.listenToEvents() from sellObject function because it is called from listenToEvents function
Deploy to private network - Benefits - as close as possible to real network - stores stuff "permanantely"
-
remember to shut down test network
-
go to private network directory
ChainList/private
-
sh startnode.sh
-
new tab go to project directory (ChainList/training/chainlist)
-
truffle test
- much slower becuase using real node
- test case 2 and 3 don't pass because need password (accounts not unlocked by default)
-
geth attach
-
unlock account that is selling
personal.unlockAccount(eth.accounts[1], "password", 3600)
-
truffle test
-
deploy contract to private node
- each time you deploy address, truffle keeps track of address at which contract was deployed in build/contracts/ChainList.json at bottom of file it says "address": ""
-
truffle migrate --reset
-
test front end npm run dev
Buy article
-
add to contract
-
if require fails (or assert, throw, revert), the spent gas up to that point isn't refunded
-
throw: legacy
-
assert: internal errors
-
require = preconditions
-
revert = other business errors. Used when condition is more complex that require
.transfer() throws revert if fails
- all contracts have their own ether balance
-
-
truffle migrate (must use --reset if not the first time since console started)
-
truffle console
-
get instance of contract
ChainList.deployed().then(function(instance){app=instance})
-
check balance of accounts
web3.fromWei(web3.eth.getBalance(web3.eth.accounts[0]), "ether").toNumber() web3.fromWei(web3.eth.getBalance(web3.eth.accounts[1]), "ether").toNumber() web3.fromWei(web3.eth.getBalance(web3.eth.accounts[2]), "ether").toNumber()
-
check object in contract
app.getObject.call()
-
sell object
app.sellObject("object1", "Description of object 1", web3.toWei(10, "ether"), {from: web3.eth.accounts[1]})
-
check that it is in contract
app.getObject.call()
-
setup event for buying
- pass in filter so it only watches event where seller is account 1
var buyEvent = app.buyObjectEvent({_seller: web3.eth.accounts[1]}, {fromBlock: 0, toBlock: 'latest'}).watch(function(error, event){console.log(event);})
-
Buy Object
app.buyObject({from: web3.eth.accounts[2], value: web3.toWei(10, "ether")})
-
check contract state
- should have buyer
app.getObject.call()
-
check balance of accounts
web3.fromWei(web3.eth.getBalance(web3.eth.accounts[1]), "ether").toNumber() web3.fromWei(web3.eth.getBalance(web3.eth.accounts[2]), "ether").toNumber()
- if buyer doesn't send proper amount of ether, error is thrown and no ether is sent (except spent gas)
Test Buy article
- update test suite
Test Buy article exceptions
-
ChainListExceptions.js
-
truffle test or truffle test test/ChainListExceptions.js
Buy article from frontend
- Update html and app.js
Buy and sell multiple objects
-
add structure to contract and add id
-
add mapping to contract
-
update functions
- don't need getObjects() because public mapping automatically creates getter
-
startup console ish
-
get instance
ChainList.deployed().then(function(instance){app=instance})
-
sell two articles from account
web3.fromWei(web3.eth.getBalance(web3.eth.accounts[1]), "ether").toNumber() web3.fromWei(web3.eth.getBalance(web3.eth.accounts[2]), "ether").toNumber()
var sellEvent = app.sellObjectEvent({}, {fromBlock: 0, toBlock: 'latest'}).watch(function(error, event){console.log(event);})
var buyEvent = app.buyObjectEvent({}, {fromBlock: 0, toBlock: 'latest'}).watch(function(error, event){console.log(event);})
app.sellObject("Object 1", "Object 1 description", web3.toWei(10, "ether"), {from: web3.eth.accounts[1]})
app.sellObject("Object 2", "Object 2 description", web3.toWei(20, "ether"), {from: web3.eth.accounts[1]})
-
test new getters
app.getObjectsForSale() app.getNumberOfObjects()
-
getter that is created for the mapping automatically app.objects(ID);
app.objects(1)
-
-
buy object
app.buyObject(1, {from: web3.eth.accounts[2], value: web3.toWei(10, "ether")})
-
app.getObjectsForSale()
- should only have one
-
check balances
web3.fromWei(web3.eth.getBalance(web3.eth.accounts[1]), "ether").toNumber()
-
app.objects(1)
- should have buyer
Update Test Suites to handle multiple arguments
Buy and sell multiple articles on frontend
Deactivate smart contract
-why? - don't need it anymore - proof of concept - shut down company - some technical or functional limitations - can't update contracts once on the chain
- functions and state variables not available anymore for future interaction
- funds are sent to address passed as parameter
- only allow contract owner to call self destruct
-
assign contract owner in constructor
-
write kill function
- only can be called by owner
- funds sent to owner
selfdestruct(owner)
-
truffle console
-
get deployed contract instance
ChainList.deployed().then(function(instance) {app=instance})
-
kill contract
app.kill({from: web3.eth.accounts[1]})
- won't work because not person who initiated contractapp.kill({from: web3.eth.accounts[0]}) - works - can still see events - can't do any calls to contract - gas is still spent if you try though - if ether is sent to dead contract, it is lost
- ens.domains.com
- allows you to change address but point to same page
- ens.domains.com
Function Modifier
- can take arguments
- _; just means run function code
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function kill() onlyOwner {
selfdestruct(owner)
}
Inheritance
-
extend contract
-
make parent contract
-
import on child contract
import "./Owned.sol"
-
inherit
contract Chainlist is Owned {}
-
-
remove stuff declared in parent class
https://solidity.readthedocs.io/en/latest/contracts.html#inheritance
DEPLOY TO RINKEBY
Setup on GitHub:
-
Make sure stuff is on github
- Git cheatsheat: ndpsoftware.com/git-cheatsheet.html
- Article for setting up and pushing code: https://product.hubspot.com/blog/git-and-github-tutorial-for-beginners
-
create directory ('docs') to move files into
-
create script to move files
deployfrontend.sh
-
run script
sh deployfrontend.sh
-
Go to Github settings
-
change source to master branch /docs folder
-
save
-
creates a link for your site Your site is ready to be published at https://connorvo.github.io/eth_chainlist/.
- still must run ganache-cli
- might have to truffle migrate
-
-
fix errors that arise
- big numbers not handled well, so in app.js add .toNumber() to objectId in reloadObjects
- this will become a problem when number gets large (JS dones't do large numbers)
- big numbers not handled well, so in app.js add .toNumber() to objectId in reloadObjects
-
redeploy front end
sh deployfrontend.sh
Deploy to Rinkeby:
-
make sure ganache isn't running
-
download blocks of rinkeby chain data ChainSkills/rinkeby
-
go to rinkeby.io and connect yourself using geth
-
download rinkeby.json into directory
- save page as
-
initialize node on network
- current directory is .
geth --datadir . init rinkeby.json
-
create some accounts
- use stronger password
geth --datadir . account new
-
script to start rinkeby node
startrinkebynode.sh
-
start node
sh startrinkebynode.sh
-
check synchronization
geth attach
eth.accounts <- check accounts
eth.syncing
-
import 3 accounts into Rinkeby import json files from keystore
- Rinkeby uses proof of authority to prevent spam attacks
- only certain nodes can authorize blocks
-
add new network to truffle.js for rinkeby (in chainlist folder)
-
unlock account before transaction
personal.unlockAccount(eth.coinbase, "INSERT PASSWORD", 600)
-
migrate to rinkeby network
- open new tab and cd into chainlist folder
truffle migrate --network rinkeby --reset
- --reset ensures latest build and force deployment
- may have to run mulitple times before it works
- can see info about transactions at rinkeby.etherscan.io
- good info about gas costs
-
run deployfrontend.sh so proper network will run depending on which account you have in metamask
-
try out website using rinkeby
- give github link to others and they can connect with their rinkeby network
DEPLOY TO MAINNET
-
shut down any running nodes
-
geth --rpc --rpcapi="personal,eth,network,web3,net"
- connects to main network
- will have to sync
-
add network to truffle.js for live network (in chainlist folder)
live: { host: "localhost", port: 8545, network_id: 1, from: ACCOUNT WHERE DEPLOY CONTRACTS FROM, <- default is coinbase gas: MAX GAS TO DEPLOY CONTRACTS }
-
unlock necessary accounts
-
open terminal and cd to directory where Dapp is
-
migrate (account must be unlocked)
truffle migrate --reset --network live
- Etherscan.io for info about transactions
- can go straight to etherscan by clicking transaction in metamask
-
deployfrontend.sh
- must do after migrating to update ChainList.json
-
Use Dapp!
- make sure using metamask on mainnet
- can use localhost 8545 if running node on computer
ERRORS
new BigNumber() not a number
- not including added parameter in html
- app.js not handling new paramete properly
Have to reload page for events (MetaMask doesn't do it)