2022. 3. 31. 14:28ㆍ개발 잡부/블록체인
1. Payable
payable은 이더를 받을 수 있게 하는 함수 유형이다.
함수를 실행하기 위해 사용료를 지불하는 것이다.
이 payable 표시가 없다면 이더를 보내려할 때 거부할 것이다.
이더리움 표시
ether또한 days나 weeks처럼 하나의 전역 단위로 동작한다.
1 ether
정답
...
// 1. 여기에 levelUpFee를 정의하게
uint levelUpFee = 0.001 ether;
modifier aboveLevel(uint _level, uint _zombieId) {
require(zombies[_zombieId].level >= _level);
_;
}
// 2. 여기에 levelUp 함수를 삽입하게
function levelUp(uint _zombieId) external payable {
require(msg.value == levelUpFee);
zombies[_zombieId].level++;
}
...
2. 출금
그렇다면 이렇게 쌓인 돈은 어떻게 인출할 수 있을까?
address변수는 transfer라는 내장 함수를 가지고 있다.
돈을 보내는 코드인데,
owner(소유자, 나)에게 보내는 코드를 내부에 짤수도 있다.
contract GetPaid is Ownable {
function withdraw() external onlyOwner {
owner.transfer(this.balance);
}
}
쌓인 자산 보기
this.balance를 통해 컨트랙트에 저장된 이더의 양을 가져올 수 있다.
정답
...
// 1. 여기에 withdraw 함수를 생성하게
function withdraw() external onlyOwner {
owner.transfer(this.balance);
}
// 2. 여기에 setLevelUpFee를 생성하게
function setLevelUpFee(uint _fee) external onlyOwner {
levelUpFee = _fee;
}
..
3. 좀비 전투
이제는 본격적으로 코딩을 해볼 시간이다.
정답
pragma solidity ^0.4.19;
import "./zombiehelper.sol";
contract ZombieBattle is ZombieHelper {
}
4. 난수 (Random Numbers)
난수는 말 그대로 무작위의 숫자다.
그렇다면 일단 프로그래밍에서처럼 rand()를 하면 될까?
블록체인 프로그래밍을 이해하려면 작업증명을 알아야 한다.
여러 컴퓨터들이 작업증명이라고 하는 어려운 문제를 푼다.
그리고 문제를 다 풀면, 함수를 실행하고 그 결과를 블록에 쌓아 배포한다.
그런데 만약, 문제를 푼 다음에 배포를 안 하면 어떻게 될까?
함수를 실행해본 다음 맘에 안 들면 배포를 안 하는 인간도 있을 것이다.
물론, 가능성이 몹시 낮기는 하지만 가능은 하다는 얘기다.
그렇기 때문에 난수생성은 이 튜토리얼을 벗어나는 얘기라고 한다.
아마 영원한 난제일수도 있다.
나중에는 이더리움 외부에서 데이터를 받아오는 oracle을 사용해볼수도 있다.
정답
...
// 여기서 시작하게
uint randNonce = 0;
function randMod(uint _modulus) internal returns(uint) {
randNonce++;
return uint(keccak256(now, msg.sender, randNonce)) % _modulus;
}
...
5. 좀비 싸움
말이 좀 긴데 좀비 싸움의 진행방식은 다음과 같다고 한다.
1. 내 좀비들 중 하나를 고르고 상대방의 좀비를 선택한다.
2. 공격하는쪽은 70%의 확률로 승리하고, 방어하는 쪽은 30%의 확률로 승리한다.
3. 전투 결과에 따라 증가하는 winCount와 lossCount가 생긴다.
4. 공격하는 쪽의 좀비가 이기면, 좀비의 레벨이 오른다.
5. 좀비가 이기든 지든, 공격하는 쪽 좀비의 재사용시간이 활성화될 것이다.
정답
...
// 여기에 attackVictoryProbability를 만들게
uint attackVictoryProbability = 70;
...
// 여기에 새로운 함수를 만들게
function attack(uint _zombieId, uint _targetId) external {
}
...
6. 공통 로직 구조 개선하기 (Refactoring, 리팩토링)
여러 함수에서 공통적으로 쓰이는,
좀비가 특정 소유자의 것인가를 확인하는 로직을
사용자 정의 함수 제어자로 통일한다.
정답
...
// 1. 여기에 제어자를 생성하게
modifier ownerOf(uint _zombieId) {
require(msg.sender == zombieToOwner[_zombieId]);
_;
}
...
// 2. 함수 정의 부분에 제어자를 추가하게:
function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal ownerOf(_zombieId) {
// 3. 이 줄을 지우게
...
7. 구조 더 개선하기
정답
...
// 1. 이 함수를 `ownerOf`를 사용하도록 변경하게:
function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) ownerOf(_zombieId) {
zombies[_zombieId].name = _newName;
}
// 2. 이 함수에도 똑같이 적용하게:
function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) ownerOf(_zombieId) {
zombies[_zombieId].dna = _newDna;
}
...
8. 공격으로 돌아가자!
정답
...
// 1. 여기에 제어자를 추가하게
function attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) {
// 2. 여기서 함수 정의를 시작하게
Zombie storage myZombie = zombies[_zombieId];
Zombie storage enemyZombie = zombies[_targetId];
uint rand = randMod(100);
}
...
9. 좀비 승리와 패배
Zombie 구조체에 win과 loss를 세는 변수를 넣는다.
정답
...
// 1. 여기에 새로운 속성을 추가하게
uint16 winCount;
uint16 lossCount;
}
...
// 2. 여기서 새로운 좀비의 생성을 수정하게:
uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime), 0, 0)) - 1;
...
10. 좀비 승리
정답
...
// 여기서 시작하게
if (rand <= attackVictoryProbability) {
myZombie.winCount++;
myZombie.level++;
enemyZombie.lossCount++;
feedAndMultiply(_zombieId, enemyZombie.dna, "zombie");
}
...
11. 좀비 패배
정답
...
} // 여기서 시작하게
else {
myZombie.lossCount++;
enemyZombie.winCount++;
}
_triggerCooldown(myZombie);
...
후기
이번 챕터에서는 새로운 것을 배우기보다는
실질적인 기능을 코딩하는 경우가 많았다.
그래서 코딩테스트를 보는것 같아 재미있었다.
확실히 자바 스크립트를 잘 쓰니까 솔리디티 하기 편한 것 같다.
'개발 잡부 > 블록체인' 카테고리의 다른 글
크립토 좀비 1-6. 앱 프론트엔드 & Web3.js (0) | 2022.03.31 |
---|---|
크립토 좀비 1-5. ERC721 & 크립토 수집품 (0) | 2022.03.31 |
크립토 좀비 1-3. 고급 솔리디티 개념 (0) | 2022.03.30 |
크립토 좀비 1-2. 좀비가 희생물을 공격하다. (0) | 2022.03.30 |
블록체인 Footprint (0) | 2022.03.30 |