Install ganache and truffle, run ganache in the background
Note that ganache is a GUI, install it using these instructions on their website.
For truffle, we can install it via npm.
# install ganache, and run it (GUI)
npm i -g truffleCreate a blank truffle project
mkdir dadc-cars
cd dadc-cars
truffle init
$EDITOR truffle-config.jsWhen editing truffle-config.js uncomment networks.development and set to match the ganache instance.
Note that on the truffle website, they tell you to use truffle unbox -
we do not want to do this for the purposes of our session.
Create and deploy the contract
truffle create contract Cars
# paste the cars contract into the new file
truffle create migration Cars
# paste the contents of `1_initial_migration.js` file into the new file,
# and replace all `Migrations` with `Cars`
truffle migrateStart the truffle REPL
REPL: Read-evaluate-print-loop
truffle console --network developmentNote that truffle console uses whatever is in truffle-config.js, so connects to ganache.
On the other hand, truffle develop will start its own simulated blockchain, which we do not want in this case.
Also, if you have used the node REPL before, this will seem familiar, with some differences:
- You may not end your statements with a
;. - Top level truffle commands are invokable, e.g.
migratewithin the REPL does the same thing astruffle migrateoutside of the REPL.
Access accounts, and get the contract
contract = await Cars.deployed()
accounts = await web3.eth.getAccounts()
accounts[0]
// should match the first account's address in ganacheBehind the scenes, truffle has done a few things to make this happen:
- Truffle has access to the compiled/ deployed contract and the ABI, which were used to create
Cars - We're creating
contractfromCars, and this usestruffle-contract- think of this as an instance.
Invoke an auto-generated getter for a public variable
numCars = await contract.numCars()
numCars.toString()
// this is a *transaction*, so we used up some gas
numCars = await contract.numCars.call()numCars.toString()
// this is a *call*, so we used up no gasWe expect the result to be '0' since we haven't run anything yet.
Note that the actual returned value is a BigNumber, so we need to use .toString() or .toNumber().
Generally speaking, use .call() where you can, as it executes locally and thus does not cost any gas.
Submit transactions only when you are doing something that changes the state of the contract.
Invoke a function
result = await contract.addCar('0x00FF00', 4, 0, 0, 0, { from: accounts[0], value: web3.utils.toWei('0.09', 'ether') })
// expect an error, as this function rejects payments less or equal to 0.1 ETH
result = await contract.addCar('0xFFFF00', 4, 0, 1800000, 1800000, { from: accounts[0], value: web3.utils.toWei('0.11', 'ether') })
// expect this to succeed - yellow car owned by account[0]
result
result = await contract.addCar('0xFF00FF', 4, 0, 1800000, 1800000, { from: accounts[1], value: web3.utils.toWei('0.11', 'ether') })
// expect this to succeed - purple car owned by account[1]
resultTry out some of the other functions. Is there anything wrong with them?
State of the contract after invoking the functions
We have previously modified the state of the contract, so let's inspect this.
numCars = await contract.numCars.call()
numCars.toString()
// should now be 2
contract.cars(1)
contract.cars(2)
// output the cars that have been saved to the mapping - auto-generated getter takes 1 arg for the keyGenerate an event
Some functions emit events, and we can tell by looking at the logs property
of the result of the contract call.
result = await contract.honkCar(1, 999, { from: accounts[0] })
// expect this to fail, other car must exist
result = await contract.honkCar(2, 1, { from: accounts[0] })
// expect this to fail, you need to own this car
result = await contract.honkCar(2, 1, { from: accounts[1] })
// expect this to succeed
result.logs[0]// event: 'CarHonk', `args.fromCar: <BN: 2>`, `args.atCar: <BN: 1>`