Local Development Environment
- IDE -
- Atom with Ethereum plugins
- Node
- upgrade to 8, if to keep system consistency, put to a user path and add that path in front of everything else
- Flattener
- use one of the flatteners to combine all imported Solidity source codes into one, this allows using Remix and verify source code with Etherscan
- https://github.com/BlockCatIO/solidity-flattener
Setup and initiate project
truffle init - a new empty project
truffle unbox - unbox a box, see http://truffleframework.com/boxes/
npm install zeppelin-solidity - highly recommended
truffle.js - configuration
./contracts - Solidity contracts
./migrations - scriptable deployment files
./test - test files
- Use Atom as IDE
- Use truffle init or truffle unbox to scaffold
- Highly recommended to install zeppelin
- Develop some contract
- Write migrate script
- truffle compile - compile
- start Ganache
- truffle console - connect to Ganache instance
- > migrate - test migrate
- use the console to do some basic interaction with deployed contract and test
- code style refer to http://truffleframework.com/tutorials/debugging-a-smart-contract
- looks like just JavaScript, nice to have it as an interactive console
- >NameOfSmartContract - print the object
- > VscToken.deployed().then(function(instance){return instance.totalSupply.call();}).then(function(value){return value.toNumber()});
- Deploy and obtain a TruffleContract abstraction - available already, just use NameOfSmartContract
- write test
(With Ganache, previous known as TestRPC)
No need to mine.
truffle develop - starts a built-in development blockchain
truffle develop --log : connect to existing session and output log
blockchain http://truffleframework.com/docs/getting_started/console
Commands (on its prompt):
- migrate - deploy contracts
- debug <Transaction ID> : id can be obtained from log if necessary
solc --bin --abi contracts/VscToken.sol -o build/ zeppelin-solidity=/home/bing/git/vsc/ethereum/node_modules/zeppelin-solidity/ --optimize --overwrite
use importPath=/path/must/from/root to include and allow directory
Deploy to testnet or live network
https://medium.com/@guccimanepunk/how-to-deploy-a-truffle-contract-to-ropsten-e2fb817870c1
First need to specify network:
in "truffle.js" put:
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*" // Match any network id
}
}
- (ganache console command) : migrate
- migrate --reset : recompile and re-deploy
- migration files
- name: n_description_of_migration.js - n is a number
- what's in that file (js)
- artifacts.require("contract_name_not_file_name") - returns the contract abstraction
- eg: var ContractTwo = artifacts.require("ContractTwo");
- module.exports - exports a function
- the function
- accept a deployer, or
- module.exports = function(deployer, network) { ... : network is a name
- module.exports = function(deployer, network, accounts) {... accounts is a list of accounts
- deployer object
- deployer.deploy(contract, args...., options) - see example below
- singleton contract -
- one instance
- DeployedContract.address = newly deployed, override previous address
- batch deployment
- pass in an array
- last one is an option include transaction parameters
- "overwrite" - true/false
- gas - gas price
- from - deploy from
- deployer.link(LIBRARY_CONTRACT, DESTINATIONS)
- link library to contracts (see example below)
- deployment examples:
- deploy in sequence: deployer.deploy(A); deployer.deploy(B);
- deploy with dependency: (ex1 below)
- deploy depend on network (defined in truffle.js) (ex2)
// Ex1:
// Deploy A, then deploy B, passing in A's newly deployed address
deployer.deploy(A).then(function() {
return deployer.deploy(B, A.address);
});
// EX2:
module.exports = function(deployer, network) {
if (network == "live") {
// Do something specific to the network named "live".
} else {
// Perform a different step otherwise.
}
}
// Deploy a single contract without constructor arguments
deployer.deploy(A);
// Deploy a single contract with constructor arguments
deployer.deploy(A, arg1, arg2, ...);
// Don't deploy this contract if it has already been deployed
deployer.deploy(A, {overwrite: false});
// Set a gas price and from address for the deployment
deployer.deploy(A, {gas: 4612388, from: "0x...."});
// Deploy multiple contracts, some with arguments and some without.
// This is quicker than writing three `deployer.deploy()` statements as the deployer
// can perform the deployment as a single batched request.
deployer.deploy([
[A, arg1, arg2, ...],
B,
[C, arg1]
]);
// External dependency example:
//
// For this example, our dependency provides an address when we're deploying to the
// live network, but not for any other networks like testing and development.
// When we're deploying to the live network we want it to use that address, but in
// testing and development we need to deploy a version of our own. Instead of writing
// a bunch of conditionals, we can simply use the `overwrite` key.
deployer.deploy(SomeDependency, {overwrite: false});
// LINKING EXAMPLE
// Deploy library LibA, then link LibA to contract B, then deploy B.
deployer.deploy(LibA);
deployer.link(LibA, B);
deployer.deploy(B);
// Link LibA to many contracts
deployer.link(LibA, [B, C, D]);
// then example
var a, b;
deployer.then(function() {
// Create a new version of A
return A.new();
}).then(function(instance) {
a = instance;
// Get the deployed instance of B
return B.deployed():
}).then(function(instance) {
b = instance;
// Set the new instance of A's address on B via B's setA() function.
return b.setA(a.address);
});
- Migration.sol contract
- required to use the migration features,
- must contain a specific interface, but free to edit
- will be first deployed for most projects and not updated again
Capability: code / instruction level step, NOT examine variable value
- (develop console) debug <Transaction ID>
- transaction id can be obtained from log if necessary
- debug commands
- ENTER: last command entered
- n : next
Contract Abstraction & Contract (*important)
http://truffleframework.com/docs/getting_started/contracts
Contract abstraction:
- Available in development console automatically in same name: YourSmartContract
- in test, obtain from artifacts.require('YourSmartContract');
Contract instance:
- Obtain instance
- YourSmartContract.deployed().then(function(instance){....
- must have already deployed (see migration)
- deployed returns a Promise, which calls callback with an instance after deployed
- YourSmartContract.new().then(function(instance) {...
- instance = YourSmartContract.at(address)
- Execute contract function
- Transaction
- cost gas, change state, asynchronous (must use "then"), return a transaction result object (not return value)
- how to do it: return instance.yourTransactionFunction(parameter1, parameter2, {from: some_address}).then(function(result){....;
- direct call means send transaction
- what returned from the function is a Promise
- use "then(function(result){" to retrieve result (transaction) - only called after transaction processed
- use "catch(function(e){... to handle error
- optionally, last parameter can provide specific details about the transaction
- the result object:
- tx : transaction hash, a string
- logs : array of events
- receipt : transaction receipt
- Call (read only)
- no-gas to call, no state change, processed immediately (no mining), expose return value (but not what returned from call()!!!)
- how to do: return instance.yourCallFunction.call(parameter1, parameter2..., {optional transaction details});}).then(function(value){.....
- what is "call" actually returned? not the value!!! a Promise
- after the promise is committed, then the callback is called with return value
- so, even direct call does not directly return result
- Send Ether
- instance.sendTransaction({...}).then(function(result){...
- just like transaction call
- return a promise
- instance.send(web3.toWei(1,"either")).then(function(result){.....
- Other members
- address : contract address
- allEvents: ()
Test Basics:
- Two ways :
- Location
- put in "test" dir: .js, .es, /es6, .jsx, .sol
- Clean room environment
- When running in Ganache / Truffle Develop, use snapshotting feature to ensure test files don't share state with each other
- When running against other clients (geth, for example) will re-deploy all migrations at the beginning of every test file
- Actually, each contract()
With Javascript / Mocha
- var YourContract = artifacts.require('Your Contract')
- contract()
- contract('Name', function (accounts) {...
- before each contract(), contracts are re-deployed so get clean contract state
- provides list of accounts to use
- web3 - configured and just works
- Assert with Chai
- expect(), assert() and should-style http://chaijs.com/
- IMPORTANT:
- YourSmartContract.deployed() returns a Promise
- it's not a result and the code promised to run has not been executed
- don't get confused, code like this in console "VscToken.deployed().then(function(instance){return instance.totalSupply.call();}).then(function(value){return value.toNumber()});" did get a result... after some unnoticeable delay... but it's an asynchronous run
- so, don't put assert after the above code - when assert is executed the promised has not been committed
- do assert in a "then"
- see https://developers.google.com/web/fundamentals/primers/promises
// this example works.
VscToken.deployed()
.then(function(instance){
console.log("Running total supply call...");
return instance.totalSupply.call();})
.then(function(value){
console.log("Extracting value..."+value);
assert.equal(value, 12000, '12000 is our totaly supply');
},
function(){console.log("Rejection called");});
- tests:
- tests:
- npm
- describe('Name', function() {...describe('#method()', function() { /// it('desc test case', function(){...
- synchronous
- asynchronous
- call "it" with a function accepting "done"
- it('some description', function(done) { .... done(err);else done();...
- even simpler: user.save(done); - done function
Technical issues costs me much time but finally...
Don't use Ganache UI, use truffle develop
Ganache with UI is very buggy. When running tests against it, the result does not repeat. Use "truffle develop"
Don't confuse Truffle Contract Abstract object with web3.
- Both can be used to call contracts so don't confuse them
- When working on Truffle, check Truffle documentation not web3
Truffle contract calls are all asynchronous
- MyContract.deployed().then(function(instance){varBalance = instance.getBalance....}); assert.equal(varBalance, ...) - what's the problem here? when executing the assert, nothing has been executed on network. do everything in "then", or "await" style
- Also mind what's returned INSIDE of "then" is not what returned FROM "then"
Error: The contract code couldn't be stored, please check your gas amount.
- This popup from time to time and according to some threads, is actually very confusing
- Reasons discussed could be:
- Attempting to migrate abstract contract
- may be caused by inconsistent parameters in function interface
- Someone suggest "rm -R build/" works