Zombies Attack Their Victims (2)

이전 포스팅에서 좀비에게 먹이를 주는 컨트렉트를 작성했다.

이제 좀비가 무슨 먹이를 먹어야할지 생각해보아야한다.

좀비의 먹이를 크립토키티로 정했다.
크립토키티를 먹이로 주기위해서는 크립토키티 스마트 컨트렉트에서 키티 DNA를 읽어와야한다.

즉, 다른 컨트렉트와 상호작용이 필요하다.


인터페이스

블록체인 상에 있으면서 소유하고있지 않은 다른 컨트렉트와 상호작용을 하기위해서는 인터페이스를 정의해야한다.

다음 예시를 보자

contract LuckyNumber {
  mapping(address => uint) numbers;

  function setNum(uint _num) public {
    numbers[msg.sender] = _num;
  }

  function getNum(address _myAddress) public view returns (uint) {
    return numbers[_myAddress];
  }
}

위 예시는 행운숫자를 각 주소에 맞는 행운숫자를 매핑해 주소의 행운 숫자를 알아낼수 있는 컨트렉트이다.

위 컨트렉트에서 외부의 누군가가 행운숫자를 얻어오려면 getNum함수를 호출해야한다.

다음은 LuckyNumber 컨트렉트와 상호작용해 getNum함수를 호출하는 인터페이스의 예시이다.

contract NumberInterface {
  function getNum(address _myAddress) public view returns (uint);
}

인터페이스 정의는 컨트렉트 정의와 유사하다.
다만, 인터페이스에서는 호출하려는 함수만 선언하고, 상태변수나 다른 함수는 정의하지 않는다.


인터페이스 활용

인터페이스가 정의되면 컨트렉트에서 인터페이스를 사용할 수 있다.
물론, 이때 호출할 함수는 public이나 external로 선언되어 있어야 한다.

다음은 좀비의 먹이로서 상호작용할 크립토키티 컨트렉트이다.

	function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
) {
    Kitty storage kit = kitties[_id];

    // if this variable is 0 then it's not gestating
    isGestating = (kit.siringWithId != 0);
    isReady = (kit.cooldownEndBlock <= block.number);
    cooldownIndex = uint256(kit.cooldownIndex);
    nextActionAt = uint256(kit.cooldownEndBlock);
    siringWithId = uint256(kit.siringWithId);
    birthTime = uint256(kit.birthTime);
    matronId = uint256(kit.matronId);
    sireId = uint256(kit.sireId);
    generation = uint256(kit.generation);
    genes = kit.genes;
}

솔리디티는 다른 프로그래밍 언어와 달리 하나 이상의 다양한 값을 반환할 수 있다.

위 크립토키티를 우리의 컨트렉트레 인터페이스 정의를 하면 다음과 같다.

	...
// 크립토키티 컨트렉트를 인터페이스로 적용하였다. 
contract KittyInterface {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ZombieFeeding is ZombieFactory {

//정의한 인터페이스를 사용하기위해 크립토키티 컨트렉트 주소를 정의하고 초기화를 한다. 
  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  KittyInterface kittyContract = KittyInterface(ckAddress);
  
  ...

다수의 반환 값 처리하기

getKetty 함수는 다수의 반환 값을 가지고 있다.

솔리디티에서는 다수의 반환 값을 다음과 같이 처리한다.

function multipleReturns() internal returns(uint a, uint b, uint c) {
  return (1, 2, 3);
}

function processMultipleReturns() external {
  uint a;
  uint b;
  uint c;
  // 다음과 같이 다수 값을 할당한다.
  (a, b, c) = multipleReturns();
}

// 혹은 단 하나의 값에만 
function getLastReturnValue() external {
  uint c;
  // 다른 필드는 빈칸으로 놓기만 하면 된다: 
  (,,c) = multipleReturns();
}

multipleReturns라는 함수를 호출하면서 인자가 나열된 순서에 맞게 원하는 변수를 채워넣어주면 함수의 반환값이 변수에 채워진다.

function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    
    // _kittyId에 맞는 Kitty를 반환
    // 반환값은 genes만 필요, 나머지는 공백지정
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    
    // _zombieId와 위에서 반환값으로 받은 kittyDna로 feedAndMultiply함수 호출
    feedAndMultiply(_zombieId, kittyDna);
  }

위 코드는 인터페이스로 정의한 kittyContractgetKitty함수를 호출하는 함수이다.


추가

좀비가 고양이를 먹은 경우 특별함을 주기 위해서 인자를 하나 추가하고
고양이를 먹은 좀비의 dna는 마지막 두자리가 99가 되도록했다.

위 조건을 포함한 전체 코드는 아래와 같다.

pragma solidity ^0.4.19;

import "./zombiefactory.sol";

contract KittyInterface {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ZombieFeeding is ZombieFactory {

  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  KittyInterface kittyContract = KittyInterface(ckAddress);

                                                            // _species 인자 추가
  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;
   
   // 만약 좀비가 고양이에서(kitty) 생성되면 좀비 dna의 마지막 2자리를 99로 설정
   if (keccak256(_species) == keccak256("kitty")) {
       newDna = newDna - newDna % 100 + 99;
   }
    _createZombie("NoName", newDna);
  }

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
  
    // "kitty"인자 추가
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }

}

이제 좀비는 다음처럼 고양이를 먹을 수 있다.

아래는 위 좀비가 고양이를 먹어서 생성된 고양이 좀비이다.
dna의 마지막 두자리가 99인것을 확인 할 수 있다.

좋은 웹페이지 즐겨찾기