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