[Klaytn][caver-java] 5. 수수료대납 컨트랙트 배포와 실행

Woody Kim
Day34 Inc.
Published in
14 min readAug 21, 2019

안녕하세요. 안녕하세요. 저는 (주)34일의 주니어 개발자 우디(Woody)라고 합니다. 이번에 다룰 주제는 클레이튼에서 caver-java를 이용하여 수수료 대납 형식으로 컨트랙트를 배포하고 실행하는 방법입니다.

수수료 대납 컨트랙트 배포

FeePayerManager를 이용하여 수수료 대납으로 컨트랙트 배포를 하기 위해 다음의 과정이 필요합니다.

  1. SmartContractDeployTransaction 객체 생성
  2. TransactionManager를 이용하여 SmartContractDeployTransaction을 Sender의 개인키로 사인하여(두번째 인자 true) FeeDelegatedSmartContractDeployTransaction 객체를 생성
  3. FeePayerManager을 이용하여 String타입의 FeeDelegatedSmartContractDeployTransaction을 실행

이를 코드로 표현하면 다음과 같습니다.

SmartContractDeployTransaction smartContractDeployTransaction 
= SmartContractDeployTransaction.create(
<fromAddress>,
<amount>,
<payload>,
<gasLimit>,
<codeFormat>
);

TransactionManager transactionManager
= new TransactionManager.Builder(caver, sender)
.setChaindId(ChainId.BAOBAB_TESTNET)
.build();

String senderRawTransaction
= transactionManager
.sign(smartContractDeployTransaction, true)
.getValueAsString();

KlayCredentials feePayer
= KlayCredentials.create(<feePayer private key>);
FeePayerManager feePayerManager
= new FeePayerManager.Builder(caver, feePayer)
.setChaindId(ChainId.BAOBAB_TESTNET)
.build();
feePayerManager.executeTransaction(senderRawTransaction);

SmartContractDeployTransaction 객체 생성에 필요한 인자는 (1)fromAddress, (2)amount, (3)payload, (4)gasLimit, (5)codeFormat 입니다.

(1)fromAddress는 컨트랙트를 배포하고자 하는 계정의 주소이며, (2)amount는 배포되는 컨트랙트로 전송할 Klay으로, BigInteger 타입의 peb단위(이더리움의 wei단위)입니다. 배포하는 컨트랙트에 Klay를 전송할 것이 아니라면, BigInteger.ZERO로 설정해주면 됩니다. (3)payload는 실제 배포되는 컨트랙트의 데이터를 필요로 합니다. (4)gasLimit은 컨트랙트 배포시에 사용되는 가스비의 최대값을 나타내며, (5)codeFormat은 BigInteger 타입으로 “0x0”을 필요로함으로 BigInteger.ZERO를 넣어주면 됩니다.

생성된 SmartContractDeployTransaction 객체는, TransacitonManager를 이용하여 sender의 개인키로 서명해야 합니다. 그리고, 두번째 인자를 true로 설정해야 수수료 대납을 할 수 있는 FeeDelegatedSmartContractDeployTransaction객체를 만들 수 있으며, 이를 getValueAsString()을 이용하여 String타입으로 만들어 줍니다.

마지막으로 FeePayerManager를 이용하여, String타입으로 만들어진FeeDelegatedSmartContractDeployTransaction FeePayer의 개인키로 서명 후 클레이튼 네트워크로 트랜잭션을 실행하면 모든 과정이 끝이 납니다.

Example :

4번째 챕터에서 CA로의 Klay전송을 보여주기 위해, fallback함수와 Klay를 transfer하는 함수를 포함한 ExampleCA라는 간단한 컨트랙트를 만들었습니다. 예제로, 해당 컨트랙트를 수수료 대납형식의 컨트랙트 배포하겠습니다.

컨트랙트를 배포하기 위해서는 일단, 배포하는 컨트랙트의 바이트코드 데이터가 필요합니다. 가장 간단하게 바이트코드 데이터를 추출하는 방법은 Klaytn IDE나 Remix를 이용하는 것 입니다.

위 사진과 같이 Klaytn IDE의 컴파일에서 Details를 클릭하면 해당 컨트랙트의 메타데이터, 바이트코드, ABI 등을 확인 할 수 있습니다.

위 사진처럼 BYTECODE부분에서 object의 데이터만 복사하여 컨트랙트를 배포할 때, 입력 데이터로 사용하겠습니다.

아래 사진은 수수료 대납형식의 컨트랙트 배포에 대한 테스트 코드입니다.

Test code : FeeDelegatedSmartContractDeploy

String 타입의 data는 ExampleCA의 바이트코드 데이터이며, SmartContractDeployTransaction 객체를 생성할 때, payload부분에 Numeric.hexStringToByteArray()함수를 이용하여 인자로 넣어주면 됩니다.

다음 사진은 테스트코드 실행에 대한 출력값입니다.

output : transactionHash, deployedContractAddress, errorMessage

테스트코드 실행에 대한 출력값으로 transactionHash와 배포된 컨트랙트의 주소, 에러메세지를 출력하였습니다.

다음 사진은 해당 트랜잭션해쉬를 클레이튼 스코프에 검색한 결과입니다.

Klaytn Scope Search : transactionHash

위 사진에서 확인할 수 있듯이, Fee Delegated Smart Contract Deploy 방식으로 컨트랙트가 성공적으로 배포되었음을 확인할 수 있습니다. Input Data는

다음 사진은 클에이튼 스코프에서 배포된 컨트랙트의 주소를 검색한 결과입니다.

Klaytn Scope Search : deployedContractAddress

수수료 대납 컨트랙트 실행

이번에는 수수료 대납으로 컨트랙트를 실행하는 것에 대해 알아보겠습니다.

FeePayerManager를 이용하여 수수료 대납으로 컨트랙트를 실행 하기 위해 다음의 과정이 필요합니다.

  1. SmartContractExecutionTransaction 객체 생성
  2. TransactionManager를 이용하여 SmartContractExecutionTransaction을 Sender의 개인키로 사인하여(두번째 인자 true) FeeDelegatedSmartContractExecutionTransaction 객체를 생성
  3. FeePayerManager을 이용하여 String타입의 FeeDelegatedSmartContractExecutionTransaction을 실행

수수료 대납을 통한 컨트랙트 실행을 하기 위해서는 실행하고자 하는 함수에 대한 바이너리 데이터를 필요로 합니다. caver-java에서는 별도로 바이너리 데이터를 만들어주는 함수가 정의되어 있지 않습니다. 따라서 web3j의 Function 객체를 만들어 아래와 같은 바이트코드 데이터를 만들어 주어야 합니다.

Function function = new Function(
<functionName>,
Arrays.asList(
<param1>,
<param2>
...),
Collections.emptyList()
);
String data = FunctionEncoder.encode(function);

Function 객체의 생성은 인자로 (1)functionName, (2)리스트타입의 인자값들, (3)반환값들이 들어갑니다. (1)functionName은 컨트랙트에서 실행하고자하는 함수의 이름을 의미하며, (2)리스트타입의 인자값들은 실행하고자하는 함수가 필요로하는 인자값들입니다.

생성된 Function 객체는 FunctionEncoder를 통해 인코딩해야 합니다.

수수료 대납으로 컨트랙트 실행에 대한 코드는 다음과 같습니다.

Caver caver = Caver.build(Caver.BAOBAB_URL);
KlayCredentials sender = KlayCredentials.create(SENDER_PRIVATE_KEY);
Function function = new Function(
<functionName>,
Arrays.asList(
<param1>,
<param2>
...),
Collections.emptyList()
);
String data = FunctionEncoder.encode(function);
SmartContractExecutionTransaction smartContractExecutionTransaction
= SmartContractExecutionTransaction.create(
<fromAddress>,
<deployedContractAddress>,
<amount>,
<payload>,
<gasLimit>
);
TransactionManager txManager
= new TransactionManager.Builder(caver, sender)
.setChaindId(ChainId.BAOBAB_TESTNET).build();
String rawTransaction
= txManager.sign(smartContractExecutionTransaction, true).getValueAsString();
KlayCredentials feePayer = KlayCredentials.create(FEEPAYER_PRIVATE_KEY);
FeePayerManager feePayerManager
= new FeePayerManager.Builder(caver, feePayer)
.setChainId(ChainId.BAOBAB_TESTNET).build();
//Sync : FeePayer ExecuteTx
KlayTransactionReceipt.TransactionReceipt transactionReceipt = feePayerManager.executeTransaction(rawTransaction);

SmartContractExecutionTransaction 객체 생성에 필요한 인자는 (1)fromAddress, (2)deployedContractAddress, (3)amount, (4)payload, (5)gasLimit 입니다.

(1)fromAddress는 컨트랙트를 실행하고자 하는 계정의 주소이며, (2)deployedContractAddress는 실행하고자 하는 컨트랙트의 주소입니다. (3)amount는 컨트랙트로 전송할 Klay으로, BigInteger 타입의 peb단위(이더리움의 wei단위)입니다. (4)payload는 deployedContractAddress로 보내는 데이터로 FunctionEncoder를 통해 인코딩된 String 타입의 data를 Numeric.hexStringToByteArray(data) 형식으로 넣어주면 됩니다. (5)gasLimit은 컨트랙트 실행시에 사용되는 가스비의 최대값을 나타냅니다.

생성된 SmartContractExecutionTransaction 객체는, TransacitonManager를 이용하여 sender의 개인키로 서명해야 합니다. 그리고, 두번째 인자를 true로 설정해야 수수료 대납을 할 수 있는 FeeDelegatedSmartContractExecutionTransaction객체를 만들 수 있으며, 이를 getValueAsString()을 이용하여 String타입으로 만들어 줍니다.

Example :

수수료 대납으로 배포된 ExampleCA의 KlayTranfer함수를 이용하여 EOA/CA로 클레이 전송하는 예제를 진행하도록 하겠습니다.

아래 코드는 ExampleCA 컨트랙트로부터 다른 ExampleCA로 0.5Klay를 전송하는 테스트 코드입니다.

Function객체의 첫번째 인자로 ExampleCA 컨트랙트의 KlayTransfer에 대한 함수의 이름인 “klayTransfer”를 두번째 인자로는 리스트타입로 klayTransfer를 실행하기 위한 인자인 toAddress와 amount값을 넣어주었습니다. 여기서 주의할 점은 주소값은 Address 타입으로, 숫자타입은 Uint256 타입으로 넣어주어야 합니다. 만약 스트링타입이면 Utf8String 타입을 사용하시길 바랍니다.

Test code : FeeDelegatedSmartContractExecutionTransaction

저는 결과를 조금 더 시각적으로 보여드리기 위해, sender, recipient, feePayer의 balance를 같이 출력하였습니다.

output : transactionHash

출력된 값을 통해 알수 있듯이, sender의 잔액이 0.5Klay만큼 줄었고 recipient의 잔액이 0.5Klay만큼 증가함을 알 수있습니다. 또한 feePayer의 잔액이 수수료만큼 감소되었다는 걸 확인할 수 있습니다.

Klaytn Scope result

위의 사진은 해당 트랜잭션의 트랜잭션해쉬값을 통해 클레이튼 스코프에서 검색한 결과 입니다. Fee Delegated Smart Contract Excuteion 타입으로 성적적으로 트랜잭션이 실행되었음을 알 수 있습니다.

이상 클레이튼에서 수수료 대납구조로 스마트 컨트랙트 배포와 실행에 대한 이야기를 마치도록 하겠습니다. 혹시 잘못된 부분이 있으면, 언제든지 말씀해주세요. 선배 개발자로서 조언도 감사하겠습니다 :)

[참고] https://docs.klaytn.com/sdk/caverjava/getting_started

--

--