View와 Pure 가스소모에 대한 진실
-
external : 다른 account 사용자가 해당 코드에 접근가능
-
internal : 코드 안에서만 동작하게 함
-
public : (external+internal)
-
pure func : state를 바꾸지않는 함수
-
view func : state를 읽는 함수
-
Pure와 View 함수는 함수가
external 일때
, 즉외부에서 함수가 호출
되었을 때Gas가 소모되지 않음
-
하지만, 다른 함수에서
내부적으로 호출
하면 가스 비용 발생
contract PureFunctionTest {
uint state;
function addNumbers(uint a, uint b) public pure returns (uint) {
return a +b ;
}
function updateState(uint a, uint b) public {
uint c = addNumbers(a,b);
state = c;
}
function addThreeNumbers(uint a, uint b, uint c) public pure returns (uint) {
uint temp = addNumbers(a,b);
uint num = addNumbers(temp,c);
return num;
}
}
- addNumbers : gas 소모 X
- updateState : 해당 함수 내에서 addNumbers 호출하기 떄문에 gas 비용 발생
- addThreeNumbers : gas 소모 X
Event 처리
- front 단에서 event 호출하는 함수 실행 시 receipt를 보고 해당 로그 반영 가능
- 이 event가 발생되었을 때 알림을 받으려면 eventSubscribe를 호출하면됨
myContract.events.MyEvent({
filter: {myIndexedParam: [20,23], myOtherIndexedParam: '0x123456789...'}, // Using an array means OR: e.g. 20 or 23
fromBlock: 0
}, function(error, event){ console.log(event); })
.on("connected", function(subscriptionId){
console.log(subscriptionId);
})
.on('data', function(event){
console.log(event); // same results as the optional callback above
})
.on('changed', function(event){
// remove event from local database
})
.on('error', function(error, receipt) { // If the transaction was rejected by the network with a receipt, the second parameter will be the receipt.
...
});
- indexed가 포함되면 검색할 수 있음(DB의 index와 동일)
- 특정 키로 검색하여 빠르게 인덱싱 가능
- 만약 index가 없다면 filter 없이 무조건 다 받고 처리하겠다.
Modifier
- 파이썬에서 function decorator와 비슷
- _의 의미
- modifier가 붙어있는 함수에 대한 내용을 실행시켜라 (c언어에서 go to와 같음)
- 컴파일 시 modifier가 붙어있는 함수에 require()문을 붙여서 같이 컴파일 시킴
- pickWinner 함수 컴파일 시 require(manager==msg.sender); 구문을 붙여 컴파일
function pickWinner() public restricted{
uint index=random()%players.length;
players[index].transfer(address(this).balance);
}
modifier restricted{
require(manager==msg.sender, "Caller is not manager");
_;
}
msg.sender
- 트랜잭션 서명을 가지고 와서 복호화를 해서 나오는 값
- 메타마스크에서 Tx confirm 버튼을 누르는 행위가 브라우저에 저장된 메타마스크 개인키를 가지고 서명을 한 것
- 이 때, 서명을 하면 v,r,s가 생성이 되어서 tx 안에 들어가고 이를 컨트랙트가 처리할 때는 v,r,s를 가지고 from을 만들어냄
- 이 from이 msg.sender 이다!
Require
- require는 파이썬의 assert와 비슷 => 조건 맞지 않으면 throw error 반환
- require 조건 맞지 않으면, 해당 Tx은 아예 없었던 것으로 됨
- 해당 Tx에 있었던 모든 State 변이가 모두 revert됨
- 아래 if 문과 동일한 문법
require(manager==msg.sender, "Caller is not manager");
if(msg.sender != manger) {
revert("Caller is not manager")
}
변수의 Private
- 만약, 변수에 private을 선언하면 해당 데이터를 스토리지에서 보지 못할까?
- 볼 수 있다!
- 아래 ownwer 데이터는 CA 어카운트 내 storage의 첫번째 슬롯에 이 값을 넣겠다고 코드가 작성이 되어 있는 것.
- 어? 이 배포된 컨트랙 주소 뭐야?
- 이 주소의 첫번째 슬롯에 저장된 값이 뭐지? 하면 바로 나옴
- 따라서 컨트랙트 짤 때
변수에 private을 넣으면 privacy가 보장된다? => No!!
contract Owner{
address private owner;
}
- 만약,
public 으로 선언하면, 자동으로 getter 메소드를 만들어준다!
- 리믹스에서 파란색으로 자동으로 owner 함수 만들어짐
- 인터페이스가 많아지기 때문에, 배포 시
Gas 비용이 높아짐!
Return
- return 되는 타입 명시해줄 때, 임시변수를 할당해주면 body에서 return을 해주지 않아도 된다
- 임시변수를 할당하면, 나중에 dApp 단을 설계할 때
인터페이스 통일
시켜줄 수 있어 가독성이 높아진다.
- 임시변수를 할당하면, 나중에 dApp 단을 설계할 때
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
function retrieve2() public view returns (uint256 num){
num = number;
}
function retrieve3() public view returns (uint256 num){ // 이렇게 return까지 해주는게 제일 CLean Code!
num = number;
return num;
}
Data Location
- storage, memory, calldata 중 하나를 명시해야하는 data type이 있음
- uint, address, bytes32, bool 같은 것들은
primitive type
이기 때문
- uint, address, bytes32, bool 같은 것들은
mapping(address => Voter) public voters;
struct Voter {
uint weight;
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
function getWeightOfVoter(address voterAddress) public view returns (uint256 weight){
weight = voters[voterAddress].weight;
return weight
}
- 위 코드 변형
- Voter voter = voters[voterAddress]; 하면 에러남(
data location 필요
하다고)
- Voter voter = voters[voterAddress]; 하면 에러남(
- 아래 코드를 보면서 이해하자
-
storage
: 해당 data type의값이 변경
될 때 -
memory
: 해당 data type의 값이변경되지 않고
, 다른 값을 그냥copy해서 대입할때
-
mapping(address => Voter) public voters;
struct Voter {
uint weight;
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
function getWeightOfVoter(address voterAddress) public view returns (uint256 weight){
Voter memory voter = voters[voterAddress]; // memory 영역에 voter 변수를 위한 공간을 할당해주세요. 그리고 Storage 영역인 voters에서 해당 값을 읽어 카피해주세요 => 읽어들이는 것이기 때문에 copy하는게 맞음
weight = voter.weight;
return weight
}
function setWeightOfVoter(address voterAddress) public returns (uint256 weight){
Voter storage voter = voters[voterAddress]; //
voter.weight += 1;
weight = voter.weight;
return weight
}