개요
이번 시간에는 이더리움의
Solidity를 활용해서 간단한 스마트 계약을 만들어 보는 시간을 가져보도록 할게요. 유언을 스마트 계약으로 처리하는 것은 블록체인을 잘 활용할 수 있는 실사용사례 중 하나일 수 있는데요. 왜냐하면 변호사나 은행 같은 중개인 없어도 계약 내용에 따라 자동 실행이 가능하고, 블록체인의 투명성과 위변조 불가한 특징을 동시에 활용할 수 있기 때문이에요. 오늘 내용은 스마트 계약이 처리되기 위한 기본 구성을 익혀나가는 것이 목적이기 때문에, 가볍게 봐주시면 될 것 같아요. 그리고
Java와 Javascript의 문법을 알고 있으면 쉽게 따라오실 수 있을거라 생각해요!준비
일단 인터넷에서 Remix IDE를 활용해서 Solidity 계약을 가상으로 테스트 할 수 있는 공간이 있는데요.
Remix IDE - Solidity 계약 에디터
위의 링크를 클릭하고 들어와서 사용하면 되요. 간단한 사용법과 구성소개는 다른 블로그글로 정리해서 향후에 첨부 하도록 할게요.
파일 생성 하기
일단 위 링크를 타고 들어와보시면 사진과 같이 VScode같은 편집기가 나올거에요. 설명을 드리자면 Remix IDE 웹 기반 통합 개발환경인데 스마트 계약을 만드는 온라인 작업실이라고 보면 좋겠네요.
여기서 왼쪽 파일 목록에서 새 파일로 유언을 뜻하는
will.sol 이라는 파일을 생성해볼게요. 여기서 sol은 solidity파일 확장자 이름이고 여기서 스마트 계약을 작성할 수 있어요. 계약 객체 생성하기
파일을 생성했으면 기본적으로 작성해줘야 하는
보일러 플레이트 코드가 있는데요. 한 번 아래와 같이 작성해 볼까요? Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Will {
}위의 코드같이 모든 솔리디티 코드에는 스마트 계약을 작성할 때 갖춰야 될 세가지 기본 요소라고 보면 좋은데요. 하나씩 설명드릴게요.
// SPDX-License-Identifier: MIT
이 주석은 SPDX 라이선스를 명시하는 역할을 하는데요. 코드 배포시 법적 문제를 방지하기 위해 필요한데, 없으면 솔리디티 컴파일러의 경고가 발생해요.
pragma solidity ^0.8.0;
이 부분은 컴파일러의 버전을 명시하는 역할을 하는데요.
솔리디티 컴파일러 버전 0.8.0 이상에서 컴파일 되어야 한다는 뜻이에요contract Will { }
여기서
Java의 class 같이 Solidity에서는contract 키워드를 사용해서 객체를 생성할 수 있어요. 바로 이 contract 객체가 블록체인에 배포될 스마트 계약이에요!
계약속성 정의하기
이제 유언장 계약에 대한 속성을 정의해줘야 하는데요. 유언에는 유언을 하는 사람, 그리고 사망 여부가 있으면 계약 주체를 정의할 수 있겠죠? 그럼 아래와 같이 작성해 볼게요.
Solidity
contract Will {
address owner;
bool deceased;
}address onwer;
address 라는 것은 solidity를 처음 공부하신다면 생소한 타입이실텐데요. 이건 이더리움의 계정 주소를 저장하기 위한 전용타입이라고 보시면 되요.bool deceased;
bool 은 Java를 할 때와 마찬가지로 불리언이겠죠? ㅎAddress 타입Address 타입은 솔리디티에서 매우 중요한 타입이에요. 이 타입으로 다른 컨트렉트와 상호작용하고 이 객체의 메소드로 여러가지 행동을 할 수 있어요.
.balance(속성)
address 타입에서 가장 많이 사용되는 속성으로 이더리움 잔액을 보여줘요.
.transfer()(메소드)
이더를 전송할 때 사용하는 메소드에요. 전송에 실패하면 에러를 던지고 revert까지 내장되어 있어요.
.call()(메소드)
이 메소드는 다른 특정함수를 호출하는 기능인데요. 이것으로도 이더를 전송할 수 있지만, 특징으로는 에러를 던지지 않고
false를 반환하기 때문에, 꼭 성공여부를 체크해줘야 해요. 그래서
transfer보다 좀 더 유연하게 사용하고 받는 쪽이 복잡한 로직을 사용할 수 있게 사용할 수 있어서 현재는 표준처럼 사용되고 있어요. 계약 생성자 작성하기
그럼 자바와 마찬가지로 생성자가 필요합니다. 이 생성자는 네트워크에 배포가 될 때 딱 한번 실행이 되는 거에요.
Solidity
contract Will {
address owner;
bool deceased;
constructor() payable {
owner = msg.sender;
deceased = false;
}
}constuctor() payable{}
생성자 정의는 자바랑 같은 데, 여기 payable이란 단어가 있어요. 이는 함수가 실행이 될 때 이더를 받을 수 있다는 뜻이에요.
payable로 선언되지 않은 함수에 이더를 보내면 트랜잭션을 실패하게되요.owner = msg.sender
여기서 또 새로운
msg 라는 변수를 확인할 수 있는데요. msg 역시 Solidity에서 사용되는 특수 글로벌 변수라고 생각하시면 될거 같아요. 이 변수는 현재 실행 중인 함수를 호출한 트랜잭션 또는 메시지에 대한 정보를 담고 있는 객체라고 보면 되요. 그래서 트랙잭션을 호출한 계정 주소를 owner에 할당하해서 계약 소유자를 설정하는 거죠!deceased = false;
생성시에는 유산을 남기려는 분이 살아계시니 바로 유산이 전송이 되지 않겠죠. 미래 시점에 deceased가 true로 트리깅이 되면 계약을 실행할 수 있게끔 속성을 정의한거에요.
가족 지갑 설정하기
이번엔 유산을 받을 가족들의 지갑을 설정해줘야 되겠죠? 여기 Will 이라는 유산 계약에서는 가족 지갑을 받을 배열 데이터 타입만 만들어 놓고, 나중에 계약 소유자가 호출해서 추가할 수 있게 만들어 줄거에요.
Solidity
address payable[] familyWallets;
mapping(address => uint) inheritance;address payable[] familyWallets;
address type의 배열을 만드는 데, 유산을 나눠줄 때 받는 사람들의 주소가 이더를 받을 수 있는 주소여야 하기 때문에
payable[] 배열로 설정해줘야 되요. mapping(address => uint) inheritance;
이건
inheritance라는 map 타입을 설정하는 것인데요. 지갑 주소에 따라 얼마만큼의 유산을 정할 것인지 정보를 담는 그릇이라고 생각하시면 되요.상속액 설정하기
이제 계약을 소유한 계약자가 가족 지갑에 따라 상속액을 정할 수 있는 함수를 만들어 줄 것인데요. javaScript의
function이랑 문법이 비슷해요! 그럼 상속액 설정과 상속액을 지급하는 함수, 그리고 사망시에 상속액이 지급되는 함수를 만들어보죠!setInheritance() - 상속자별 상속액 설정 함수
Solidity
function setInheritance(address payable wallet,uint amount) public {
familyWallets.push(wallet);
inheritance[wallet] = amount;
}function setInheritance(address payable wallet,uint amount) public
기존 javaScript 함수와 같이 정의해서 사용하며 되요! 여기서는
public 지시어로 누구나 이 함수를 호출할 수 있게 되요. 하지만 사실, 누구나 이 함수를 호출하면 안되겠죠? 그 부분은 아래에서 추가적으로 설명해볼게요. 또 상속액을 설정하기 위해서는 지급가능한 주소와, 상속액을 매개변수로 받아야 겠죠? 그래서 위와 같이 매개변수를 설정했어요!familyWallets.push(wallet); inheritance[wallet] = amount;
familyWallets은 배열이니까 push로 지갑을 추가할수 있겠죠? 그리고 inheritance mapping 타입에 주소와 상속액을 mapping 시켜요! payout() - 상속액 지급 함수
Solidity
function payout() private {
for (uint i = 0; i < familyWallets.length; i++) {
familyWallets[i].transfer(inheritance[familyWallets[i]]);
}
}function payout() private
for (uint i = 0; i < familyWallets.length; i++)
hasDeceased()- 사망시 실행되는 함수 설정
Solidity
function hasDeceased() public {
deaceased = true;
payout();
}- 함수가 실행되면
deaceased를 true로 업데이트하고payout()메서드가 실행이 되면서 자동으로 유산을 지급하는 계약이 트리거가 되는거에요. 그리고 이렇게 완성이 된 것같지만, 치명적인 오류가 존재해요. 즉 현재 코드로서는 아무나 이 계약을 실행할 수 있다는 것이 문제이죠. 그래서 생소하겠지만함수 수정자를 만들어야 해요.
함수 수정자 설정하기
함수 수정자(
modifier)는 자바에서 AOP(Aspect-Oriented Programming)과 비슷한 역할을 하는 Solidity만의 문법인데요. 비즈니스 로직에서 분리된 공통 관심사를 설정할 수가 있는 것이죠! 여기서는 함수를 실행하는 사람이 계약 소유자인지 체크하기 위해서 사용된다 거나, 계약이 실행되기 위한 조건을 체크하는 용도로 사용이 되어요! modifier onlyOwner- 계약소유자 본인 체크 수정자
Solidity
modifier onlyOwner() {
require(msg.sender == owner);
_;
}modifier onlyOwner()
require(msg.sender == owner);
modifier mustBeDeceased- 유언자의 사망여부 체크
Solidity
modifier hasDeceased() {
require(deceased == true);
_;
}- 그럼 이
hasDeceased함수에서도deceased가true인지 체크하고 상태가 확인이 되면 다음 로직으로 넘어간다라고 이해가 되겠죠?
![[Solidity] 유언 스마트 계약 만들어보기](/_next/image?url=https%3A%2F%2Fwww.notion.so%2Fimage%2Fattachment%253A94c93052-dd63-4f3a-80b7-6af22500d58a%253A16.png%3Ftable%3Dblock%26id%3D2889c76c-6cb4-8056-8ad8-e48de41cebd3%26spaceId%3Db4216657-966f-4c29-ae8c-42f6c4adb66d&w=3840&q=75)