> mkdir MetaCoin
> cd MetaCoin
> truffle unbox metacoin
unbox는 truffle에서 제공하는 환경 설정을 미리 해주는 것이다.
> mkdir myproject
> cd myproject
> truffle init
truffle init을 하게 되면 생기는 폴더와 파일이 있다.
- contracts/: Solidity contract들을 담고 있다.
- migratiosn/: script식의 deployment file들을 담고 있다.
- test/: application이나 contracts를 테스트해볼 수 있는 file들을 담고 있다.
- truffle.js: truffle의 환경 설정 파일이다.
이 때 생기는 Migrations.sol은 배포 과정을 도와주는 스마트 컨트랙트다.
> truffle compile
이 명령어로 프로젝트에 있는 solidity 코드를 컴파일하게 된다. 이전에 컴파일 되어 있는 .sol 파일은 제외하고 .sol 파일을 컴파일한다. 만약 override해서 모든 컴파일을 하고 싶을 때는 --all 옵션을 사용한다.
unbox한 MetaCoin 프로젝트를 compile했다.
Compile하고 나면 build라는 디렉토리가 새로 생성된다. 이 파일은 not edit해야 한다. 그렇지 않으면 새로 컴파일하면 override될 수도 있고 잘못 배포될 수도 있다.
Migration은 이더리움 네트워크에 contracts를 배포하는 것을 돕는 JavaScript file들이다.
var MyContract = artifacts.require("MyContract");
module.exports = function(deployer) {
deployer.deploy(MyContract);
}배포를 하게 된 contract는 artifacts.require()를 통해 contract abstraction을 리턴하게 된다.
Filename: migrations/1_initial_migration.js
truffle은 새로운 contract를 배포할 때마다 앞에 prefix의 숫자를 입력한다.
contract를 synchronously하게 배포해야할 수도 있다.
deployer.deploy(A);
deployer.deploy(A);또는
deployer.deploy(A).then(function() {
return deployer.deploy(B, A.address);
})이렇게 사용할 수도 있다.
deployer API는 이 페이지 밑에서 다루도록 한다..
배포할 때 network 상태에 따라 조건부로 deployment를 실행시킬 수도 있다.
module.exports = function(deployer, network) {
if (network == "live") {
// Do something specific to the network named "live".
} else {
// Perform a different step otherwise.
}
}배포할 때 list of accounts도 가능하다.
module.exports = function(deployer, network, accounts) {
// Use the accounts within your migrations.
}deployer는 migrations을 편리하게 해주는 많은 함수를 가지고 있다.
이 함수를 호출하게 되면 배포 이후 contract address가 set된다.
마지막 argument는 optional object다. 이 object에는 overwrite 키가 포함될 수 있다. 이 대신 gas, from 같은 transaction parameters가 들어갈 수 있다.
만약 overwrite가 false로 지정되어 있다면 이미 배포되어 있다면 배포를 하지 않겠다는 의미다. 만약 overwrite false를 set하지 않고 배포하면 새로운 address로 overwrite된다. overwrite와 같은 속성을 이용해, 외부 smart contract를 사용할 때 유용하다.
// 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, {overwirte: false});
// Set a maximum amount of gas 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
// 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 conditions, we can simply use the `overwrite` key
deployer.deploy(SomeDependency, {overwrite: false]});이미 배포된 library 또는 여러 contracts를 연결한다. destinations는 single contract일 수도 있고 multiple contracts일 수도 있다.
// 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])promise와 같이 배포를 순차적으로 할 수 있다.
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);
})Truffle에는 자동화 된 테스트 framework가 기본으로 제공하여 contract를 쉽게 테스트 할 수 있다. 이 framework로 쉽고 관리가 쉽게 contract를 testing할 수 있다. Truffle testing framework는 두 가지 testing 방법을 제공한다.
- In Javascript, for exercising your contracts from the ouside world, just like your application
- In Solidity, for exercising your contracts in advanced, bare-to-the-metal scenarios
두 가지 스타일의 test는 각자 장단점이 있다.
모드 테스트 파일들은 ./test 디렉토리에 저장되어 있어야 한다. Truffle은 .js, .es, .es6, .jsx, .sol 의 확장자 파일만을 테스트 파일로 취급한다.
> truffle test
> truffle test ./path/to/test/file.js --network [networkName]
Truffle은 테스트 파일을 실행할 때 클린 룸 환경을 제공합니다. Ganache 또는 Truffle Develop에 대한 테스트를 실행할 때 Truffle은 테스트 파일이 서로 상태를 공유하지 않도록 고급 스냅 샷 기능을 사용합니다. Truffle은 go-ethereum과 같은 다른 Ethereum 클라이언트를 실행할 때 모든 테스트 파일의 시작 부분에 모든 마이그레이션을 재배포하여 테스트 할 새로운 계약 세트를 확보합니다.
Ganache와 Truffle Develop는 자동화 된 테스트를 실행할 때 다른 클라이언트보다 훨씬 빠릅니다. 또한 Truffle은 테스트 런타임을 거의 90 % 단축하는 특별한 기능을 갖추고 있습니다. 일반 작업 과정에서 일반 개발 및 테스트 중에 Ganache 또는 Truffle Develop을 사용하고 실제 또는 프로덕션 네트워크에 배포 할 때 go-ethereum 또는 다른 공식 Ethereum 클라이언트에 대해 한 번 테스트를 실행하는 것이 좋습니다.
Truffle은 Mocha 테스팅 프레임워크와 Chai assertions를 사용한다.
USE CONTRACT() INSTEAD OF DESCRIBE()
describe 함수를 사용하는 것과 같지만 contract 함수는 clearn-room feature를 제공한다.
- contract 함수를 실행하기 전에 contract는 이더리움 클라이언트에 redeploy된다. 따라서 테스트를 clean contract 상태로 진행할 수 있다.
- contract 함수는 테스트용 이더리움 클라이언트에서 사용할 수 있는 accounts 리스트를 제공한다.
MOCHA에서 제공하는 describe() 또한 사용할 수 있다. 그러나 이 때엔 clean-room feature를 제공하지 않는다.
USE CONTRACT ABSTRACTIONS WITHIN YOUR TESTS
specific한 contract와 interact하기 위해서 artifacts.requre() 메소드를 사용해서 contract abstraction을 사용한다.
Example
test 코드는 .js 파일로 작성된다. 예제 코드는 truffle documents를 참고하길 바란다.
Solidity test contract는 Javascript test처럼 testing하는데 사용된다. When truffle test is run, they will be included as a separate test suite per test contract.
Truffle은 default assertion library를 제공한다.
Truffle은 내가 만들어서 배포한 contract와 interaction을 쉽게 하도록 할 수 있다.
일반적으로 Ethereum network에 읽고 쓰는 것은 구분된다. write하는 것은 보통 trasaction이라 불리고, reading data는 보통 call이라 불린다.
Transaction은 network의 상태를 변화시킨다. Transaction은 즉각적으로 처리되지 않기 때문에 return value를 받을 수 없다. 따라서 return value를 주지 않는다. 대신 transaction의 id 값을 준다.
정리해서 transaction:
- Cost gas (Ether)
- Change the state of the network
- Aren't processed immediately
- Won't expose a return value (only a transaction id)
Call는 transaciton과 다르다. Calls는 구동하는데 free하다. Call은 즉각적으로 return value를 받을 수 있다.
정리해서 call:
- Are free (do not cost gas)
- Do not change the state of the network
- Are processed immediately
- Will expose a return value
Transaction이냐 Call이냐는 데이터를 쓰고 읽느냐에 달려 있고, 이것이 network의 state를 변경시키냐에 달려 있다.
Contract abstractions는 wrapper code다. 이를 통해 쉽게 배포된 contract와 communicate한다. Truffle에서는 truffle-contract를 사용한다. Abstraction을 이용해 배포된 smart contract의 함수를 쉽게 사용할 수 있게 된다.
MegaCoin contracts:
function sendCoin(address receiver, uint amount) returns(bool sufficient) {
if (balances[msg.sender] < amount) return false;
balances[msg.sender] -= amount;
balances[receiver] += amount;
Transfer(msg.sender, receiver, amount);
return true;
}var account_one = "0x1234..."; // an address
var account_two = "0xabcd..."; // another address
var meta;
MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.sendCoin(account_two, 10, {from: account_one});
}).then(function(result) {
// If this callback is called, the transaction was successfully processed.
alert("Transaction successful!")
}).catch(function(e) {
// There was an error! Handle it.
})sendCoin함수를 direcly하게 호출할 수 있다. 이는 call이 아닌 transaction을 발생시킬 것이다.- transaction이 성공적으로 처리되면 callback 함수가 호출될 것이다. 따라서 스스로 transaction을 계속해서 체크할 필요가 없다.
- sendCoin의 파라미터는 2개지만, 추가로 object가 파라미터로 들어간다. 이는 transaction을 할 때 반드시 필요한 객체이다.
MetaCoin.sol:
function getBalance(address addr) returns(uint) {
return balances[addr];
}var account_one = "0x1234..."; // an address
var meta;
MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.getBalance.call(account_one, {from: account_one});
}).then(function(balance) {
// If this callback is called, the call was successfully executed.
// Note that this returns immediately without any waiting.
// Let's print the return value.
console.log(balance.toNumber());
}).catch(function(e) {
// There was an error! Handle it.
})What's intersting here:
.call()함수를 explicitly하게 호출해야 한다. 이 함수를 호출함으로써 Ethereum network가 Eethereum network의 상태를 변화시키지 않겠다고 의도하는 것과 같다.- transaction id를 받는 대신 call이 성공하면
return value를 받게 된다. Note that since the Ethereum network can handle very large numbers, we're given a BigNumber object which we then convert to a number. - // Big Number가 왜 계속 나오는겨?..;
Warning: We convert the return value to a number because in this example the numbers are small. However, if you try to convert a BigNumber that's larger than the largest integer supported by Javascript, you'll likely run into errors or unexpected behavior.
var account_one = "0x1234..."; // an address
var account_two = "0xabcd..."; // another address
var meta;
MetaCoin.deployed().then(function(instnace) {
meta = instance;
return meta.sendCoin(account_two, 10, {from: account_one});
}).then(function(result) {
// result is an object with the following values:
//
// result.tx => transaction hash, string
// result.logs => array of decoded events that were triggered within this transaction
// result.receipt => transaction receipt object, which includes gas used
// We can can loop through result.logs to see if we triggered the Transfer event.
for (var i = 0; i < result.logs.length; i++) {
var log = result.logs[i];
if (log.event == "Transfer") {
// We found the event!
break;
}
}
}).catch(function err) {
// There was an error! Handle it.
})transaction을 만들게 되면 result obejct를 받게 된다. 이 result object에는 transaction에 대한 중요한 정보가 들어 있다.
- result.tx (string) - Transaction hash
- result.logs (array) - Decoded events (logs)
- result.receipt (object) - Transaction receipt
deployed 함수를 이용해 배포된 contract의 abstraction을 사용했다. 이제는 새로운 our own contract를 배포하고자 한다. 이 때 .new() 함수를 사용한다.
MetaCoin.new().then(function(instance) {
// Print the new address
console.log(instance.address);
}).catch(function(err){
// There was an error! Handle it.
});contract address를 가지고 있는 경우, 새로 해당 contract를 abstract해서 해당 contract를 사용할 수 있다.
var instance = MetaCoin.at("0x1234...");만약 Ether를 보내게 되면, 받는 contract에서는 fallback function을 호출하게 된다.
instance.sendTransaction({...}).then(function(result) {
// Same transaction result object as above.
});instance.send(web3.toWei(1, "ether")).then(function(result) {
// Same result object as above.
});truffle.js 파일에 truffle의 configuration을 설정한다.
버전 2.0에서 버전 3.0으로 업데이트되면서 configuration 부분이 변경되었다. 첫째, 더 이상 기본 네트워크가 없다.
rpc항목이 제거되었다. 그 자리에 대신 명명된developmentnetwork가 있다.developmentnetwork는 specified한 network를 선택하지 않았을 때는 자동으로 연결되는 network다. 즉, network의 기본 값으로 사용한다.developmentconfiguration을 완전히 제거할 수도 있다. 하지만truffle migrate --network ropsten과 같이 network를 명시하지 않으면 Truffle은 error를 일으킨다.
2.0 버전의 default networks
module.exports = {
rpc: {
host: "127.0.0.1",
port: 8545
}
};3.0 버전
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*"
}
}
};networks 옵션은 required다. networks 옵션을 지정해주지 않으면 cntract deployed가 안된다. other networks와 더 연결하기 위해 환경설정하기 위해서는 name과 network id를 추가해준다.
network의 이름은 다른 목적의 network를 지칭할 때 사용한다. specific한 network를 사용하기 위해서는
> truffle migrate --network live
와 같이 해당 network name으로 실행한다.
Example:
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*" // match any network
},
live: {
host: "178.25.19.88", // Random IP for example purposes (do not use)
port: 80,
network_id: 1, // Ethereum public network
// optional config values:
// gas
// gasPrice
// from - default address to use for any transaction Truffle makes during migrations
// provider - web3 provider instance Truffle should use to talk to the Ethereum network.
// - function that returns a web3 provider instance (see below.)
// - if specified, host and port are ignored.
}
}각각의 네트워크에 transaction option을 주지 않으면 다음과 같은 default 값이 매겨진다.
- gas: Gas limit used for deploys. Default is 4712388.
- gasPrice: Gas price used for deploys. Default is 100000000000 (100 Shannon).
- from: From address used during migrations. Defaults to the first available account provided by your Ethereum client
- provider: Default web3 provider using host and port options:
new Web3.providers.HttpProvider("http://<host>:<port>")
각각의 network에 host / port 또는 provider를 정의할 수 있지만 둘이 동시에 정의하지는 못한다. 만약 HTTP provider를 사용하는 경우 host와 port를 사용한다. 만약 HDWalletProvider와 같은 Custom Provider를 사용하기 위해서는 provider를 사용해야 한다.
더 읽어보기..

