반응형
작성 계기
- Web3를 통한 프로젝트의 경우 truffle / hardhat 등의 Smart Contract을 위한 대중성있는 블록체인 프레임워크가 존재한다.
- Java에서도 Web3j 모듈에서 다양하게 지원하긴 하지만, Web3 프로젝트에 비해 그 과정이 상당히 복잡하고 러닝커브가 높다.
- Web3에 비해 커뮤니티 풀이 생각 외로 적고, 프로젝트 전체 플로우에 대해 참조할만한 블로그도 없어서 직접 작성하게 됐다.
사용 스택
- Spring Boot 2.7 & Gradle 7.0
- JDK 11
- Solidity 0.8.19
위 기술 스택으로 사용한 이유
- Gradle 8.0 이상의 프로젝트 (JDK 17 / Spring Boot 3.0)에서는 아직 지원이 안 되는듯하여 이슈로 올라와있다.
- Solidity 0.8.20 이상의 경우 Ganache에서 해당 버전 이상에서 "Invalid opcode" 에러가 발생한다.
1. Spring Project 생성
- 위 사용 가능 버전에 참고하여 프로젝트를 생성한다.
- external depndencies
- solidity-gradle-plugin
- org.web3j:core
2. Solidity 파일 생성
- sol 파일 위치에 대해서는 DSL 에서 처리할 수 있으나, 기본적으로 제공되는 $projectDir/src/solidity 하위에 위치하도록 한다.
- Contract 문법 등에 대해서는 주제와 떨어진 내용이니 따로 설명하지 않는다.
3. Web3j network에 배포
- Smart Contract를 사용하기 위해서는 작성한 Contract를 Ethereum Network에 배포해야 한다.
- 또한, 이를 배포하기 위해서는 작성한 Contract abi 또는 binary 파일을 Web3 나 Web3j를 통해 배포해야 한다.
- 보통 NPM Package를 통해 배포하면 Web3 (Javascript 기반)를 사용하고
- Gradle / Maven 등의 Java / Kotlin 프로젝트를 통해 배포하면 Web3j를 사용한다.
- 내가 사용한 언어 모델은 Java라서 Web3j를 사용했다.
3-1. Web3j 사용에 대하여
- Web3j에서는 Contract라는 Class를 통해 Contract 배포에 대한 편의성을 제공해준다.
- Contract 클래스는 기본적으로 추상 클래스 타입이고 다음과 같은 생성자를 제공해준다.
@SuppressWarnings({"WeakerAccess", "deprecation"})
public abstract class Contract extends ManagedTransaction {
...
protected Contract(
String contractBinary,
String contractAddress,
Web3j web3j,
TransactionManager transactionManager,
ContractGasProvider gasProvider) {
this(
new EnsResolver(web3j),
contractBinary,
contractAddress,
web3j,
transactionManager,
gasProvider);
}
...
}
- 나는 위 클래스를 활용하여 Web3jHelper라는 클래스를 구현했다.
- 이 클래스는 하위 클래스로 위 클래스를 품고 있고, 생성할 때 binary code와 Gas 비용, Contract 생성자 정보만 넘겨서 처리하도록 간편화하여 구현했다. 코드는 다음과 같다.
class Web3jHelper {
class InnerClass: Contract {
constructor(byteCode: String,
contractAddress: String,
web3j: Web3j,
transactionManager: TransactionManager,
gasProvider: ContractGasProvider
): super(byteCode, contractAddress, web3j, transactionManager, gasProvider)
companion object {
private const val HOST = "http://localhost:7545"
private const val EMPTY = "" // Contract Address
fun create(byteCode: String, gasProvider: ContractGasProvider) : InnerClass {
val web3j = Web3j.build(HttpService(HOST))
val account = web3j.ethAccounts().send().accounts[0]
val transactionManager = ClientTransactionManager(web3j, account)
return InnerClass(byteCode, EMPTY, web3j, transactionManager, gasProvider)
}
fun getAccounts(): List<String> {
val web3j = Web3j.build(HttpService(HOST))
return web3j.ethAccounts().send().accounts
}
}
fun deploy(encodedConstructor: String): InnerClass {
return deploy(InnerClass::class.java, web3j, transactionManager, gasProvider, contractBinary, encodedConstructor, BigInteger.ZERO)
}
...
}
- Host 정보는 테스트를 위해 하드코딩했지만, Property 파일을 통해 따로 빼 주는 것이 좋다.
- 해당 클래스에는 도메인 로직에 해당하는 Contract 메서드들만 구현하여 생성하는 것이 좋다(위 getAccounts()와 같은)
4. Ganache Test Network에 배포
- 보통 Smart Contract을 Public Chain에 배포할 수도 있지만, 일단 Gas Cost가 비싸다..
- 그래서 Private Network를 사용했는데, Private Network를 사용하는 다양한 이유가 있긴 하지만, 나는 테스트를 위해 사용했다.
- Ganache는 Truffle 에서 제공하는 Test Network인데, GUI부터 Network UX까지 구성하기 쉽게 되어있다.
- 시작하기 위해서는 [Ganache Download] 사이트에 접속하여 받고 띄운다.
5. 배포 시 유의사항
- Ethereun Network는 각 Network 마다 정해진 Gas Limit이 존재한다. => Ganache에서 Gas 초과가 발생하면 Limit을 늘리도록 한다.
- Network에서 Account 정보는 위 사진의 MNEMONIC 12 자리를 통해 만들어진다. => 간략하게 얘기하면 계정이 모자르지는 않는다.
- 배포 시에 from address 혹은 to address를 적을 때에는 위 사진과 같이 해당 Network에서 제공되고 있는 address 정보를 사용해야 한다.
6. 결론
- Web3를 사용하면 Truffle / Hardhat 등의 간편한 Framework이 존재한다.
- 내가 Java 개발자라고 해서 Web3보다 Web3j로 개발하는 것이 빠르다라고 생각했지만, 생각보다 Web3j의 Community Pool이 너무 적기도 하고 Web3로 개발하는 것이 생각 이상으로 너무 간단했었다.
- 또한, Latest Version에 대한 지원 부족으로 맞는 버전을 계속해서 찾아야했던 수고로움이 많았다.
한 줄 요약
- Web3 쓰자..
반응형
LIST
'언어 > Solidity' 카테고리의 다른 글
Soldity - Storage Collision (0) | 2023.09.27 |
---|---|
Solidity Gas Cost (0) | 2023.09.26 |