[크립토좀비] 레슨3. 고급 솔리디티 - 시간 단위, 함수 제어자

가영·2021년 4월 9일
0

솔리디티

목록 보기
5/7
post-thumbnail

시간 단위 (Time units)

솔리디티는 시간을 다룰 수 있는 단위를 제공한다 🙂

now 변수를 쓰면 현재의 유닉스 타임스탬프(1970년 1월 1일부터 지금가지의 초단위 합) 값을 얻을 수 있다! 신기하네

유닉스 타임은 전통적으로 32비트 숫자로 저장되네. 이는 유닉스 타임스탬프 값이 32비트로 표시가 되지 않을 만큼 커졌을 때 많은 구형 시스템에 문제가 발생할 "Year 2038" 문제를 일으킬 것이네. 그러니 만약 우리 DApp이 지금부터 20년 이상 운영되길 원한다면, 우리는 64비트 숫자를 써야 할 것이네. 하지만 우리 유저들은 그동안 더 많은 가스를 소모해야 하겠지. 설계를 보고 결정을 해야 하네!

또, 유닉스 타임스탬프 값을 직접 사용하기가 쉽지 않아서,

seconds, minutes, hours, days, weeks, years 같은 시간 단위도 제공해준다! 우리가 이런 단위를 써주면, 솔리디티에서 유닉스 초단위로 변환을 해준다.

1 minutes == 60
1 hours == 3600
1 days == 86400

참고로 1을 계산할때도 복수형으로 그냥 써줘야한다..😱


+) 구조체를 인수로 전달하기

우리는 private 또는 internal 함수의 argument로 구조체의 storage 포인터를 전달할 수 있다.

function _doStuff(Zombie storage _zombie) internal {
  // _zombie로 할 수 있는 것들을 처리
}

public 함수 & 보안

우리가 전에 만들었던 feedAndMultiply 함수를 다시 보자.

function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) public {
    require(msg.sender == zombieToOwner[_zombieId]);
  
    Zombie storage myZombie = zombies[_zombieId];
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
  
    if (keccak256(_species) == keccak256("kitty")) {
      newDna = newDna - newDna % 100 + 99;
    }
    _createZombie("NoName", newDna);
  }

이 함수의 제어자를 보면 public으로 선언돼있어서 어느 사용자든 자신이 가진 좀비에게 먹이를 줄 수 있었다.

하지만 이렇게 되면 게임이 아니잖아..! 🥺

이 함수를 어떻게 사용했었는지 다시 생각해보면,

  1. 좀비가 크립토 키티를 먹는다.
  2. 크립토 키티의 dna를 가져온다. 👉🏻 feedOnKitty()
  3. 좀비의 원래 dna와 크립토 키티의 dna가 합쳐져서 새로운 좀비의 dna가 결정된다. 👉🏻 feedAndMultiply()
  4. 새로 만들어진 dna로 좀비를 생성한다.

그니까 우리는 feedOnKitty() 안에서만 feedAndMultiply()가 호출될 수 있게 바꿔줘야겠다 ❗ 그러려면 함수의 제어자를 intermal로 만드는 방법이 있겠다.

이렇게 ~

function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal {
  ...
}

인수를 가지는 함수 제어자

이전 포스트에서 onlyOwner라는 간단한 예시를 봤다. 근데 함수제어자는 사실 함수처럼 argument를 받을 수 있다! 🤣

// 사용자의 나이를 저장하기 위한 매핑
mapping (uint => uint) public age;

// 사용자가 특정 나이 이상인지 확인하는 제어자
modifier olderThan(uint _age, uint _userId) {
  require (age[_userId] >= _age);
  _;
}

// 차를 운전하기 위햐서는 16살 이상이어야 하네(적어도 미국에서는).
// `olderThan` 제어자를 인수와 함께 호출하려면 이렇게 하면 되네:
function driveCar(uint _userId) public olderThan(16, _userId) {
  // 필요한 함수 내용들
}

위의 예시에서는 orderThan() 이라는 modifier로 사용자가 특정 나이 이상인지 확인하고, 이 modifier를 사용해 driveCar()라는 함수를 나이가 16살 이상인 유저만 호출할 수 있도록 만들었다.

신기하다. ✨

제어자는 안의 로직에서 require()를 많이 사용한다.
require()로 통과하면 호출한 함수의 내용을 실행할 수 있도록 _; 를 넣어준다.

우리는, 게임에서 특정 레벨 이상인지 확인하는 modifier를 만들어볼 것이다.
다음과 같이 코드를 작성하면 된다!

modifier aboveLevel(uint _level, uint _zombieId) {
  require(zombies[_zombieId].level >= _level);
  _;
}

그럼 이런식으로 쓸 수 있겠지: 레벨 2이상인 사용자만 좀비의 이름을 바꿀 수 있는 changeName() 호출 가능 💨

function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].name = _newName;
  }

😊

0개의 댓글