blockchain

truffle framework pet-shop tutorial

환경설정

tuffle을 설치하고 pet-shop으로 초기화를 해줍니다.

npm install -g truffle
mkdir pet-shop-tutorial
cd pet-shop-tutorial
truffle unbox pet-shop

스마트컨트랙트 작성

contracts/Adoption.sol을 작성합니다.

adopt_입양 함수

 function adopt(uint petId) public returns(uint) {
        require(petId >= 0 && petId <= 15);
        adopters[petId] = msg.sender;
        return petId;
    }

pet의 아이디가 0이상 15이하일 때. address형 변수 adopters[인자로 받은 반려동물의 아이디]에 메세지 발신자를 넣고, 반려동물의 아이디를 리턴합니다.

getApoters_입양자 함수

    function getAdopters() public returns (address[16]) {
        return adopters;
    }

입양자를 리턴합니다.

전체 소스는 이렇습니다.

pragma solidity ^0.4.4;
//컴파일러의 버전을 지정합니다.
contract Adoption {
     //Adoption 이라는 컨트랙트를 선언합니다.
    address[16] public adopters;

    //adopters라는 변수를 선언합니다. adress는 배열이고 adopters라는 변수는 16개의 주소를 가집니다.
    function adopt(uint petId) public returns(uint) {
        require(petId >= 0 && petId <= 15);
        adopters[petId] = msg.sender;
        return petId;
    }

    function getAdopters() public returns (address[16]) {
        return adopters;
    }
}

컴파일

truffle develop
truffle(develop)> compile

개발 모드에서 컴파일합니다.

마이그레이션

migrations폴더안에 2_deploy_contracts.js를 만듭니다.

const Adoption = artifacts.require("Adoption");
module.exports = (deployer) => {
  deployer.deploy(Adoption);
 };

이후 마이그레이션을 진행합니다.

truffle(develop)> migrate
#output
Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x0d0da62e193abf827bf2e186f12a95a3340fdbaa0a59196948b001d25b1bb474
  Migrations: 0x29f13cbf33be55df7caabb86c78002e161881584
Saving successful migration to network...
  ... 0x7905d6a99cdb816cac533a6800d8c9e663b23267f7f14facecb146db9b28c4d7
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying Adoption...
  ... 0xfd0023f020e0d6d9c91e95f9dc39efea2869b5150f5a36faf16ef452bbdad999
  Adoption: 0xd2cb516049609da7c745810fe272e236fb032260
Saving successful migration to network...
  ... 0x8901f0f786338e678f2af46b23d3b68cdbcb0a04f8004f1d0a2c2954bbf854c8
Saving artifacts...

테스트

테스트를 진행하기 위해서 test폴더에 TestAdoption.sol 를 작성합니다.

testUserCanAdoptPet()
    function testUserCanAdoptPet() {
        uint returnedId = adoption.adopt(8);
        uint expected = 8;
        Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recorded.");
    }

입력값(8)로 Adoption.sol의 adopt를 호출합니다. 예상 값 8을 선언하고 Assert.equal에 입력값, 실패값, 실패 메세지를 전달합니다.

 function adopt(uint petId) public returns(uint) {
        require(petId >= 0 && petId <= 15);
        adopters[petId] = msg.sender;
        return petId;
    }

testGetAdopterAddressByPetId()
    function testGetAdopterAddressByPetId() {
        address expected = this;
        address adopter = adoption.adopters(8);
        Assert.equal(adopter, expected, "Owner of pet ID 8 should be recorded.");
    }

변수 this는 현재 계약의 주소를 나타냅니다.

testGetAdopterAddressByPetIdArray()
 function testGetAdopterAddressByPetIdInArray() {
        address expected = this;
        address[16] memory adopters = adoption.getAdopters();
        Assert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded.");
    }

adopters는 배열이고 애완동물 8을 입양한 첫 입양테스트기 때문에 컨트랙트 주소와 배열에서의 위치 8을 비교합니다.

최종적인 소스는 이렇습니다.

pragma solidity ^0.4.11;
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";
contract TestAdoption {
    Adoption adoption = Adoption(DeployedAddresses.Adoption());
    function testUserCanAdoptPet() {
        uint returnedId = adoption.adopt(8);
        uint expected = 8;
        Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recorded.");
    }

    function testGetAdopterAddressByPetId() {
        address expected = this;
        address adopter = adoption.adopters(8);
        Assert.equal(adopter, expected, "Owner of pet ID 8 should be recorded.");
    }

    function testGetAdopterAddressByPetIdInArray() {
        address expected = this;
        address[16] memory adopters = adoption.getAdopters();
        Assert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded.");
    }
}

/src/js/app/js를 수정합니다.

web3js를 로딩합니다

if (typeof web3 !== 'undefined') {
  App.web3Provider = web3.currentProvider;
 } else { 
  App.web3Provider = new Web3.providers.HttpProvider('http://localhost:9545');
 }
 web3 = new Web3(App.web3Provider);

스마트컨트랙트를 인스턴트화합니다

$.getJSON('Adoption.json', function(data) {
 var AdoptionArtifact = data;
 App.contracts.Adoption = TruffleContract(AdoptionArtifact);
 App.contracts.Adoption.setProvider(App.web3Provider);
 return App.markAdopted();
});

ui 업데이트합니다

var adoptionInstance;
  App.contracts.Adoption.deployed().then(function(instance) {
  adoptionInstance = instance;
  return adoptionInstance.getAdopters.call();
  }).then(function(adopters) {
  for (i = 0; i < adopters.length; i++) {
   if (adopters[i] !== '0x0000000000000000000000000000000000000000') {
    $('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);
   }
  }
  }).catch(function(err) {
  console.log(err.message);
  });

adopt()함수를 조작합니다

var adoptionInstance;
 web3.eth.getAccounts(function(error, accounts) {
  if (error) {
   console.log(error);
  }
  var account = accounts[0];
  App.contracts.Adoption.deployed().then(function(instance) {
   adoptionInstance = instance;
   return adoptionInstance.adopt(petId, {from: account});
  }).then(function(result) {
   return App.markAdopted();
  }).catch(function(err) {
   console.log(err.message);
  });
 });

최종적인 소스는 이렇습니다

App = {
   web3Provider: null,
   contracts: {},
  init: function() {
    // Load pets.
    $.getJSON('../pets.json', function(data) {
     var petsRow = $('#petsRow');
     var petTemplate = $('#petTemplate');
 
    for (i = 0; i < data.length; i ++) {
      petTemplate.find('.panel-title').text(data[i].name);
      petTemplate.find('img').attr('src', data[i].picture);
      petTemplate.find('.pet-breed').text(data[i].breed);
      petTemplate.find('.pet-age').text(data[i].age);
      petTemplate.find('.pet-location').text(data[i].location);
      petTemplate.find('.btn-adopt').attr('data-id', data[i].id);
      petsRow.append(petTemplate.html());
     }
    });
   return App.initWeb3();
   },
  initWeb3: function() {

  if (typeof web3 !== 'undefined') {
     App.web3Provider = web3.currentProvider;
    } else { 
     App.web3Provider = new Web3.providers.HttpProvider('http://localhost:9545');
    }

    web3 = new Web3(App.web3Provider);
    return App.initContract();
   },

  initContract: function() {
  $.getJSON('Adoption.json', function(data) {
     var AdoptionArtifact = data;
     App.contracts.Adoption = TruffleContract(AdoptionArtifact);
     App.contracts.Adoption.setProvider(App.web3Provider);
     return App.markAdopted();
    });
    return App.bindEvents();
   },

  bindEvents: function() {
    $(document).on('click', '.btn-adopt', App.handleAdopt);
   },

  markAdopted: function(adopters, account) {
  var adoptionInstance;
  App.contracts.Adoption.deployed().then(function(instance) {
  adoptionInstance = instance;
  return adoptionInstance.getAdopters.call();
  }).then(function(adopters) {
  for (i = 0; i < adopters.length; i++) {
   if (adopters[i] !== '0x0000000000000000000000000000000000000000') {
    $('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);
   }
  }
  }).catch(function(err) {
  console.log(err.message);
  });
   },
   handleAdopt: function(event) {
    event.preventDefault();
    var petId = parseInt($(event.target).data('id'));
  var adoptionInstance;
  web3.eth.getAccounts(function(error, accounts) {
   if (error) {
    console.log(error);
   }
   var account = accounts[0];
   App.contracts.Adoption.deployed().then(function(instance) {
    adoptionInstance = instance;
    return adoptionInstance.adopt(petId, {from: account});
   }).then(function(result) {
    return App.markAdopted();
   }).catch(function(err) {
    console.log(err.message);
   });
  });
   }
  };
 
  $(function() {
   $(window).load(function() {
    App.init();
   });
  });

메타마스크의 시드를 다음으로 설정합니다.

candy maple cake sugar pudding cream honey rich smooth crumble sweet treat

로그인하고, 테스트넷을 localhost:9545로 설정합니다.

npm run dev

서버를 시작하면 끝입니다.

참고

https://boostlog.io/@junp1234/how-to-make-a-dapp-of-a-pet-shop-using-solidity-and-truffle-5ab200c50814730093a2ec91