How to SCORE #3
ICX Transfer, Exception Handling with fallback & payable
이번 시리즈를 이해하기 위해서는 Python
언어 및 ICON 이 제공하는 CLI 개발도구인 T-Bears 및 ICON SDK 활용법에 대한 약간의 사전지식이 필요합니다.
두번째 파트에서는 DB Abstraction 에 대해 살펴보았습니다. 혹시 아직 못보신 분이 있으시면 파트 2을 먼저 보시는 것을 권장합니다.
시리즈의 세번째 파트에서는 SCORE가 ICX
를 전송 받기 위해 구현해야할 fallback
메소드와 @payable
데코레이터, ICX
전송과 예외 처리에 대해 살펴보겠습니다.
Table of Contents
Two Cases of Transferring ICX to SCORE
- Invoking fallback method
- Invoking external SCORE method
fallback & payable
- payable
- fallback
ICX Transfer & Exception Handling
- Exception Handling
- self.icx.transfer
- self.icx.send
Hands-on Exercise
- fallback & payable
- payable with external methods
- Exception Handling
Two Cases of Transferring ICX to SCORE
Invoking fallback method
T-Bears CLI의 transfer
, ICONex의 Transfer
를 통해 SCORE로도 ICX
를 전송할 수 있으며, 이러한 경우 자동적으로 SCORE의 fallback
메소드가 호출됩니다.
단순한 ICX
전송에 해당하는 JSON-RPC Request는 예시와 같은 형식으로 요청할 수 있으며, T-Bears CLI의 transfer
, ICONex의 Transfer
를 통해 ICX
를 전송하는 경우, 이러한 형식을 갖춘 JSON-RPC Request를 전송하게 됩니다.
{
"jsonrpc": "2.0",
"method": "icx_sendTransaction",
"id": 1234,
"params": {
"version": "0x3",
"from": "hxbe258ceb872e08851f1f59694dac2558708ece11",
"to": "cxb0776ee37f5b45bfaea8cff1d8232fbb6122ec32",
"value": "0xde0b6b3a7640000",
"stepLimit": "0x12345",
"timestamp": "0x563a6cf330136",
"nid": "0x3",
"nonce": "0x1",
"signature": "VAia7YZ2Ji6igKWzjR2YsG..."
}
}
to :
ICX
를 전송받을 주소로, 예시로 제공된 JSON-RPC Request의 경우 SCORE로ICX
를 전송하고 있습니다.value : 전송하는
ICX
의 양으로0xde0b6b3a7640000
은hex
값으로 10진수로 환산하자면10¹⁸ loop = 1 ICX
에 해당합니다.
Invoking external SCORE method
ICX
를 전송하는 것은 stateDB의 상태를 변화시키는 트랜잭션을 발생시키는 것으로, 데이터를 읽어오기만 하는 external
readonly
메소드를 호출하는 경우에는 ICX
를 전송할 수 없습니다.
T-Bears CLI의 sendtx
, ICONex Contract
를 통한 external
메소드 호출과 같이 SCORE의 특정 메소드를 호출하며 ICX
를 함께 전송할 수 있으며, 이러한 경우 fallback
메소드가 호출되지 않습니다.
SCORE external
메소드 호출에 해당하는 JSON-RPC Request 는 예시와 같은 형식으로 요청할 수 있으며, T-Bears CLI의 sendtx
, ICONex의 Contract
를 통해 external
메소드를 호출하며 ICX
를 함께 전송하는 경우, 이러한 형식을 갖춘 JSON-RPC Request를 전송하게 됩니다.
{
"jsonrpc": "2.0",
"method": "icx_sendTransaction",
"id": 1234,
"params": {
"version": "0x3",
"from": "hxbe258ceb872e08851f1f59694dac2558708ece11",
"to": "cxb0776ee37f5b45bfaea8cff1d8232fbb6122ec32",
"value": "0xde0b6b3a7640000",
"stepLimit": "0x12345",
"timestamp": "0x563a6cf330136",
"nid": "0x3",
"nonce": "0x1",
"signature": "VAia7YZ2Ji6igKWzjR2YsGa2m53n...",
"dataType": "call",
"data": {
"method": "transfer",
"params": {
"to": "hxab2d8215eab14bc6bdd8bfb2c8151257032ecd8b",
"value": "0x1"
}
}
}
}
to :
ICX
를 전송받을 주소로, 예시로 제공된 JSON-RPC Request의 경우 SCORE로ICX
를 전송하고 있습니다.value : 전송하는
ICX
의 양으로0xde0b6b3a7640000
은hex
값으로 10진수로 환산하자면10¹⁸ loop = 1 ICX
에 해당합니다.data : 호출하고자 하는 SCORE의 메소드에 대한 정보들로, 예시의 JSON-RPC Request의 경우
to
와value
를 인자로 입력받는 SCORE의transfer
메소드를 호출하고 있습니다.datatype :
call
을 통해 SCORE의 메소드를 호출할 수 있습니다.
지금까지는 EOA -> SCORE
에 해당하는 ICX
전송으로 사용자가 전자서명을 통해 트랜잭션을 생성하는 경우에 대해 알아보았습니다.
이번 파트의 후반부에서 이어질 ICX Transfer & Exception Handling에서는 개인키를 가지고 있지 않은 SCORE가 ICX
를 전송하는 self.icx.transfer
와 self.icx.send
에 대해 살펴보도록 하겠습니다.
JSON-RPC API에 대한 자세한 정보는 링크를 참고해주세요.
fallback & payable
payable
SCORE의 메소드가 ICX
를 전송받기 위해서는 반드시 @payable
로 장식되어 있어야 합니다. 만약 @payable
로 장식되어 있지 않은 메소드를 호출하며 ICX
를 전송하는 경우, 해당 트랜잭션은 실패합니다.
@payable
@external
def icxAcceptable(self):
pass
@payable
로 장식되어 있는icxAcceptable
메소드는ICX
전송과 함께 호출되더라도 해당 트랜잭션이 실패하지 않습니다.
@external
def icxUnacceptable(self):
pass
@payable
로 장식되어 있지 않은icxUnacceptable
메소드는ICX
전송과 함께 호출되는 경우 해당 트랜잭션은 실패합니다.
fallback
fallback
메소드는 SCORE가 다른 메소드 호출 없이 단순히 ICX
만을 전송 받는 경우 실행됩니다. 따라서 fallback
메소드는 @external
로 장식되어 외부로 부터 호출되는 것을 금지하고 있습니다.
만약 fallback
메소드가 @payable
로 장식되어 있지 않은 경우, 해당 SCORE는 메소드 호출과 함께 ICX
를 전송하는 것이 아닌 단순한 ICX
전송을 받을 수 없습니다.
@external
에 대해서는 이어지는 시리즈에서 살펴보도록 하겠습니다.
케이스 별로 SCORE 프로젝트를 생성하여 ICX
전송 결과를 확인해 봅시다.
Case 1. @payable
없이 fallback
구현
tbears init without_payable WithoutPayable
def fallback(self):
pass
생성한 SCORE 프로젝트를 T-Bears 환경에 배포해 봅시다.
tbears deploy without_payable
tbears txresult 0xe7e323a75…
테스트 지갑을 통해 배포한 SCORE에 ICX
를 전송해 봅시다.
@payable
없이fallback
메소드를 구현하는 경우 SCORE는 단순한ICX
전송을 받을 수 없습니다.
tbears transfer -f hxe7af… cx55b65… 1e18
tbears txresult 0x0103e67…
Case 2 & 3 에 대하여서도
deploy
부터scoreAddress
를 확인까지 동일한 과정을 수행합니다. 따라서ICX
전송 결과를 제외한 부분은 생략하였습니다.
Case 2. @payable
과 함께 fallback
구현
tbears init with_payable WithPayable
@payable
def fallback(self):
pass
@payable
과 함께 fallback
메소드를 구현한 SCORE에 ICX
를 전송하고 트랜잭션의 결과를 확인해 봅시다.
@payable
과 함께fallback
메소드를 구현하는 경우, 단순한ICX
전송이 일어날 때 실행될 로직을 정의할 수 있습니다.
Case 3. fallback
미구현
tbears init no_fallback NoFallback
fallback
메소드를 구현하지 않은 SCORE에 ICX
를 전송하고 트랜잭션의 결과를 확인해 봅시다.
ICX Transfer & Exception Handling
IconScoreBase
,utility function
과 같이 아직까지 살펴보지 않은 부분들은 이어지는 시리즈를 통해 살펴보기로 하고, 이번 파트에서는 설명하지 않고 넘어가도록 하겠습니다.
Exception Handling
SCORE
에서 발생한 Exception
을 처리하는 경우 IconServiceBaseException
을 상속받아 해당 Exception
에 대한 처리를 하기보다는 revert
함수를 통한 Exception
처리를 권장합니다.
revert
:IconScoreBase
에서utility function
으로 제공되는 기능으로,revert
함수가 호출되는 경우RevertException
을 발생시키고, 트랜잭션에 의해 변화된 상태를 이전 상태로 복구시킵니다.
revert(message: str)
의도하지 않은 상황이 발생하는 경우,
revert
함수를 호출하는 것으로 해당 트랜잭션에 의한 상태 변화를 강제로 되돌릴 수 있습니다.
self.icx.transfer
self.icx.transfer(receiver: Address, amount: int)
tranfer
메소드를 통해 amount
만큼의 ICX
를 receiver
주소로 전송합니다.
ICX
전송이 성공적으로 실행되는 경우 True
를 반환하지만, SCORE의 balance
가 충분하지 않아 ICX
전송이 실패하는 경우, Out of balance
라는 메시지와 함께 InvalidParamsException
을 발생시킵니다. 발생한 Exception에 대한 별도의 처리가 없다면 트랜잭션은 실패하며, 이전까지의 상태 변화를 되돌립니다.
try/except
를 통해 발생하는InvalidParamsException
을 처리할 수 있습니다.
self.icx.send
self.icx.send(receiver: Address, amount: int)
send
메소드를 통해 amount
만큼의 ICX
를 receiver
주소로 전송합니다.
ICX
전송이 성공적으로 실행되는 경우 transfer
와 마찬가지로 True
를 반환합니다. 하지만 SCORE의 balance
가 충분하지 않아 ICX
전송이 실패하는 경우 send
는 InvalidParamsException
을 발생시키지 않고 False
를 반환합니다. send
에 의한 ICX
전송이 실패하더라도 트랜잭션은 실패하지 않습니다.
if/else
를 통해ICX
전송이 실패한 경우에 대한 처리가 가능합니다.
Hands-on Exercise
지난 파트 2 에서는 샘플 코드를 작성하며 VarDB
, ArrayDB
, DictDB
를 사용해 보았습니다. 이번에는 파트 2 에서 작성한 코드에 세번째 파트에서 살펴 본 revert
, fallback
, @payable
를 활용하는 부분을 추가로 작성해 봅시다.
fallback & payable
간단하게 SCORE가 단순한 ICX
를 전송받을 수 있도록 @payable
과 함께 별다른 로직을 수행하지 않는 fallback
메소드를 구현해 봅시다.
@payable
def fallback(self):
pass
payable with external methods
fallback
이 아닌 다른 external
메소드들도 @payable
로 장식되어 있는 경우 메소드 호출과 함께 ICX
를 전송받을 수 있습니다.
@payable
@external
def deposit(self):
pass
ICONex를 통해 deposit
메소드 호출과 함께 ICX
를 전송해 봅시다.
status
를 통해deposit
메소드 호출과 함께ICX
를 전송하는 트랜잭션이 성공적으로 실행된 것을 확인할 수 있습니다.
Exception Handling
revert
함수를 통해 10 ICX
이상의 전송은 받지 않도록 fallback
메소드의 로직을 구현해 봅시다.
self.msg.value
는loop
단위입니다.1 ICX = 10¹⁸ loop
@payable
def fallback(self):
if self.msg.value >= 10000000000000000000:
revert("ICX amount must be lower than 10")
작성한 SCORE를 배포하고, ICX
를 전송한 결과를 확인하는 것으로 세번째 파트를 마치겠습니다.
SCORE 배포부터
scoreAddress
를 확인하는 과정에 대한 설명은 생략합니다.
1 ICX
를 전송한 트랜잭션이 성공하였습니다.
10 ICX
를 전송한 트랜잭션이 실패하고,message
가 반환되었습니다.
이것으로 How to SCORE 시리즈의 세번째 파트를 마치겠습니다.
다음 파트에서는 @external
& @eventlog
데코레이터에 대해 알아보도록 하겠습니다.
ICON 과 관련된 추가적인 질문사항이 있는 경우, ICON 의 공식 개발자포럼 혹은 페이스북 그룹에 질문하시면 답변을 얻으실 수 있습니다.