이더(돈)를 받을 수 있는 특별한 제어자이다.
함수를 실행하기 위해 컨트랙트에 일정 금액을 지불하게 한다.
contract OnlineStore {
function buySomething() external payable {
// 함수 실행에 0.001이더가 보내졌는지 확실히 하기 위해 확인:
require(msg.value == 0.001 ether);
// 보내졌다면, 함수를 호출한 자에게 디지털 아이템을 전달하기 위한 내용 구성:
transferThing(msg.sender);
}
}
웹에서는 이 부분을 호출하기 위해서
// `OnlineStore`는 자네의 이더리움 상의 컨트랙트를 가리킨다고 가정해야 한다.
OnlineStore.buySomething({from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001)})
주의할 점은 payable로 표시되지 않았는데 이더를 보내려 한다면, 함수에서 트랜잭션을 거부한다.
이더를 보내고 출금을 하지 않으면 계좌에 저장되고 갇힌다.
출금 시스템
contract GetPaid is Ownable {
//Owner 인지 체크
function withdraw() external onlyOwner {
//계좌에 있는 금액을 트렌스퍼한다.
owner.transfer(this.balance);
}
}
초과 입금시 나머지를 돌려주려주어야 한다.
uint itemfee = 0.001 ether;
msg.sender.transfer(msg.value - itemFee); //입금하는 사람에게 출금하고 있다.
난수를 만들려면 keccak256해시 함수를 쓰면 된다.
// Generate a random number between 1 and 100:
uint randNonce = 0;
uint random = uint(keccak256(now, msg.sender, randNonce)) % 100;
randNonce++;
uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;
nonce: 딱 한번만 사용되는 숫자이다?!!
똑같은 입력으로 두 번 이상 동일한 해시 함수를 실행할 수 없게 한다
토큰은 기본적으로 몇몇 공통 규약을 따르면 스마트 컨트랙트이다.
즉 다른 모든 컨트랙트가 사용하는 표준 함수 집합이라는 뜻
ERC20 토큰들은 똑같은 이름의 동일한 함수 집합을 공유하기 때문에 이 토큰들에 똑같은 방식으로 상호작용이 가능하다.
만약 새로운 토큰을 추가하고 싶으면 데이터베이스에 단순히 새 컨트랙트 주소를 추가하면 된다.
게임에서 사용자들이 좀비를 거래/판매 하는 경매나 중계 로직을 구현 안 해도 된다
contract ERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
function balanceOf(address _owner) public view returns (uint256 _balance);
function ownerOf(uint256 _tokenId) public view returns (address _owner);
function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;
}
function balanceOf(address _owner) public view returns (uint256 _balance);
//Address 입력시 그 계정에 balance가 얼마나 있는지 조회할 수 있다.
function ownerOf(uint256 _tokenId) public view returns (address _owner);
//해당 토큰 아이디에 address를 리턴해준다.
uint8은 8비트 데이터를 저장한다. 저장할 수 있는 가장 큰 수는 2^8-1=255이다
uint8 number = 255;
number++;
언더플로우는 유사하게 0에서 1을 빼면 255와 같아진다는 것이다.
이를 방지하기 위해 OpenZepplin은 SafeMath라는 라이브러리를 베공한다
using SafeMath for uint256; //라이브러리를 사용하겠다고 정의
uint256 a = 5;
uint256 b = a.add(3);
uint256 c = a.mul(2);
내부소스 하나만 들여보면
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
assert는 require과 비슷하다. 값이 false일 때 반환한다.
차이점은 require은 실패시 가스비가 반환되지만 assert는 사용하면 가스비가 반환되지 않고 false로 끝난다.
따라서 극단적인 오류의 경우에만 사용한다. 오버플로우나 언더플로우 등에서!
이더리움 네트워크는 노드로 구성되어 있고, 노드들은 JSON-RPC라는 언어로만 소통한다
{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","gas":"0x76c0","gasPrice":"0x9184e72a000","value":"0x9184e72a","data":"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}],"id":1}
이는 사람이 읽기에 불편해서 인터페이스로 쉽게 구현하는 라이브러리가 있다. 바로 Web3.js이다.
설치는 여러가지 방법으로 할 수 있다.
// NPM을 사용할 때
npm install web3
// Yarn을 사용할 때
yarn add web3
// Bower를 사용할 때
bower install web3
스크립트를 가져오는 방법은
<script language="javascript" type="text/javascript" src="web3.min.js">
</script>
프로바이더를 설정하는 것은 우리 코드에 읽기와 쓰기를 처리하려면 어떤 노드와 통신을 해야하는지 설정하는 것이다
Infura는 빠른 읽기를 위한 캐시 계층을 포함하는 다수의 이더리움 노드를 운영하는 서비스이다.
나만의 노드를 설정하지 않고 이더리움 블록체인과 메세지를 주고 받을 수 있다.
var web3 = new Web3(new Web3.providers.WebsocketProvider("wss://mainnet.infura.io/ws"));
DAPP의 경우 블록체인에 write를 한다. 그럼 이 사용자들이 개인키로 트랜젝션에 서명하는 방법이 필요한데
MetaMask 브라우저 확장 프로그램을 사용할 수 있다.
window.addEventListener('load', function() {
// Web3가 브라우저에 주입되었는지 확인(Mist/MetaMask)
if (typeof web3 !== 'undefined') {
// Mist/MetaMask의 프로바이더 사용
web3js = new Web3(web3.currentProvider);
} else {
// 사용자가 Metamask를 설치하지 않은 경우에 대해 처리
// 사용자들에게 Metamask를 설치하라는 등의 메세지를 보여줄 것
}
// 이제 자네 앱을 시작하고 web3에 자유롭게 접근할 수 있네:
startApp()
})
web3.js는 address와 ABI가 필수로 있어야한다.
address는 나중에 스마트 컨트랙트와 통신을 하기 위해 배포 후 주어지는 주소이다.
ABI는 함수들과 기타 정보들을 json으로 모아놓은 형태이다.
// Instantiate myContract
var myContract = new web3js.eth.Contract(myABI, myContractAddress);