2023. 7. 11. 16:18ㆍBlockchain/Smart contract
EVM 계열 블록체인에 smart contract를 배포하기 위해 로직을 작성하는 IDE는 아마 web IDE인 Remix를 가장 많이 사용할 것이다. 이번 포스팅에는 Remix를 사용해서 solidity contract의 compile 및 deploy 하는 방법을 공유하려 한다.
Solidity sample code
Remix에서 기본적으로 제공하는 1_Storage.sol
코드를 사용하여 진행해 볼것이다. remix에 처음 접속하면 "File explorer" 탭에 default_workspace 안에 해당 컨트랙트 예제가 존재한다. 간단하게 코드 구성만 확인해보고 넘어간다.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.2 <0.9.0;
/**
* @title Storage
* @dev Store & retrieve value in a variable
* @custom:dev-run-script ./scripts/deploy_with_ethers.ts
*/
contract Storage {
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
}
pragma solidity >=0.8.2 <0.9.0;
- Solidity에는 가장 먼저 compiler 버전을 명시해야 한다. 배포한 solidity contract에 사용될
solc
의 정확한 버전을 명시해야 하며, 이 예제에서는 0.8.2이상 0.9.0미만 버전의 compiler에 대응될 수 있다는 뜻이다.
- Solidity에는 가장 먼저 compiler 버전을 명시해야 한다. 배포한 solidity contract에 사용될
contract Storage { ... }
- 한 solidity contract에는 전체 contract 범위를 표현하는
contract
keyword를 통해 name을 선언해야 한다. 해당 contract에서는 없지만 만약 상속 받아야 하는import
contract가 있을 시is
를 통해 외부 contract를 선언해 준다(e.g.contract Storage is ERC20 { ... }
). 마찬가지로 해당 contract는 가지고 있지 않지만 contract 실행 시 initialize가 필요하다면contract
keyword 내에constructor()
를 통해 로직을 정의한다.
- 한 solidity contract에는 전체 contract 범위를 표현하는
uint256 number;
uint256
타입의number
라는 명칭의 state 저장소를 선언한다.
function store(uint256 num) public { ... }
store
명칭의 함수를 정의하여 state 변경을 실행한다.public
keyword를 통해 외부에서도 접근 가능함을 정의한다.
function retrieve() public view returns (uint256) { ... }
retrieve
명칭의 함수를 정의하여 저장된 state를 조회한다.public
keyword를 통해 외부에서도 접근 가능함을 정의하며view
keyword로 조회 함수임을 정의한다. Return값은uint256
단일 변수이다.
앞서 확인한 것처럼 store
를 통해 숫자를 저장하고 retireve
를 통해 저장된 숫자를 조회하는 단순한 contract이다. Solidity를 작성하기 위한 세부적인 문법은 기회가 되면 정리를 해볼것이다.
cf) Remixd
번외로, default_workspace처럼 IDE 내부에 directory를 설정하여 contract를 작성할 수도 있지만 로컬 디렉토리 내에 있는 contract를 가져와서 개발하는 기능을 Remix에서 제공한다. Remixd라는 daemon을 사용하면 구현 가능하다. Remixd는 npm을 통해 설치할 수 있다.
$ npm install -g @remix-project/remixd
Global로 설치한 remixd를 cli를 통해서 구동할 수 있다. -s
flag를 통해 연결할 directory를 설정한다.
$ remixd -s ./[SOLIDITY_DIR]
Remixd를 동작 시킨 후에 Remix에서 connect를 수행한다.
위 그림에서처럼 "connect to localhost"를 선택하면 아래와 같이 remixd와 연결을 진행하겠냐 하는 alert가 확인된다.
Alert 정보를 확인하고 연결을 진행한다.
이후에 위 예시처럼 로컬 디렉토리 상의 contract를 remix에서 사용할 수 있다.
Compile
Compiler 탭으로 들어가면 선택한 sol 파일에 대한 compile을 수행할 수 있다. 파란색 compile 버튼을 클릭하면 작성한 contract 코드에 대해 compile을 수행한다.
실제 compile 완료 시의 화면이다. 완료하면 즉각적으로 custom IPFS에 publish하거나 Swarm에 publish 할 수 있다.
자측 하단의 "Compilation Details"를 클릭하면 compile한 상세 결과를 확인할 수 있다. 여기 params 중 deploy에 가장 중요한 것은 ABI
와 bytecode
이다.
ABI
ABI(Application Binary Interface)는 외부에서 contract interaction을 위해 해당 contract의 interface 형식을 표현한 것이다. 1_Storage.sol
contract를 compile한 후 확인되는 ABI는 아래와 같다.
[
{
"inputs": [],
"name": "retrieve",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "num",
"type": "uint256"
}
],
"name": "store",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
각 function 별로 JSON 형태로 정의되며, 확인되는 field의 specification을 간단히 정리한다.
name
각 function의 이름을 정의한다.1_Storage.sol
은store
,retrieve
두개의 function을 가지고 있다.type
function의 타입을 정의한다. 일반적인 기능 구현은"function"
, constructor는"contructor"
, Eth receive function은"receive"
, default로 실행되거나 ETH를 전송할 때 실행되는"fallback"
으로 구성된다.1_Storage.sol
의 경우 execute/query의 function만 가지고 있으므로,store
,retrieve
모두"function"
으로 설정된다.inputs
function이 입력받는 object를 정의한다.name
: parameter의 이름type
: parameter의 타입internalType
: Solidity 내부에서 사용되는 타입, 만약 입력 타입이 contract에 정의된 struct라면 아래와 같이 표시 될 수 있다.
{
"internalType": "struct Storage.StructName",
"name": "structExample",
"type": "tuple"
}
outputs
function 수행 시 return되는 object를 정의한다. 내부 필드는 위inputs
와 동일하다.stateMutability
해당 function의 state 변경 가능성을 정의한다.stateMutability
에서 확인 가능한 옵션은 아래와 같다.pure
: state를 변경할 수 없고, state의 값을 read 할 수 도 없다.view
: state를 변경할 수 없지만, state의 값을 read 할 수 있다.payable
: 해당 function이 ETH를 수령할 수 있다.nonpayable
: 해당 function이 ETH를 수령할 수 없다.
Bytecode
ABI가 contract와의 interaction을 위해 사용된다면, bytecode는 실제 EVM에서 동작하도록 solidity가 변환뙨 정보이다. Bytecode는 모두 "opcode"로 분류되어 EVM 상에서 로직으로 동작한다. 1_Storage.sol
을 compile한 후 확인되는 bytecode는 아래와 같다.
{
"functionDebugData": {},
"generatedSources": [],
"linkReferences": {},
"object": "6080604052348015...",
"opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE ...",
"sourceMap": "199:356:0:-:0;;;;;;;;;;;;;;;;;;;"
}
앞선 Ethereum gas에서도 언급되었지만, 실제 contract가 ethereum에 등록되는 object
와 이 코드가 어떤 기능을 수행하는지 확인 할 수 있는 opcodes
가 중요하다. opcodes
는 bytecode를 byte 단위로 정해진 기능을 뜻한다. 각 opcode의 기능이 정의된 예시를 아래 표에서 살펴본다. OP code는 여기서 확인할 수 있다.
OP code | Name | Minimum gas | Description |
---|---|---|---|
60 | PUSH1 | 3 | Place 1 byte item on stack |
52 | MSTORE | 3 | Save word to memory |
34 | CALLVALUE | 2 | Get deposited value by the instruction/transaction responsible for this execution |
위에서 확인되는 object
는 60/80/60/40/52/34/...로 구성되어 있고 "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE ...
와 대응이 일치하는 것을 확인 가능하다.
Contract 배포 시에는 object
만 ethereum으로 전달되면 된다.
Setting env & Deploy
Deploy 탭으로 들어가면 위와 같은 화면을 확인할 수 있다.
Environment
가장 먼저 Remix를 이용해 contract를 배포할 환경을 선택할 수 있다. Ethereum fork 버전 별로 Remix VM을 설정하여 간편하게 테스트를 진행 할 수 있다. 또한 "Injected Provider - MetaMask"를 통해 metamask extension을 이용하여 지갑에 연결한 외부 EVM에 배포할 수 있으며 "WalletConnect"로 다른 지갑을 사용할 수도 있다. 지갑을 이용하여 ethereum mainnet에 contract를 배포한다면 지갑에 gas fee가 존재해야 한다. 또한 여러 버전의 provider를 제공해서, 각 provider를 이용하여 contract를 배포할 수도 있다.
로컬호스트를 이용하거나 직접적으로 JSON RPC에 연결하여 배포한다면 "Custom - External Http Provider"를 선택하여 URL를 입력해서 사용할 수도 있다. 연결 시 주의할 점은 external provider 선택 시 나타나는 가이드와 같이 geth 동작 시에 --http.corsdomain
옵션을 주어 Remix에서 접근할 수 있도록 cross orgin을 설정해 주어야 한다. 실제 로컬호스트에 연결을 하면 위와 같이 environment 아래에 networkid도 geth 동작 시 설정한 1317로 확인이 되며 geth의 account가 자동으로 설정된다.
Account
Environment를 설정했다면 contract 배포 주체인 account도 설정 가능하다. VM을 선택했다면 VM 자체에서 제공하는 account를 사용해서 배포할 수 있으며, MetaMask 등 지갑을 사용한다면 사용하기로 설정한 account가 자동으로 선택된다. External HTTP provider를 선택하여도 자동으로 선택된다.
Gas limit
Remix에서 contract 배포나 execute를 위한 tx의 gas limit을 설정한다.
Value
Value 필드는 transaction 전송 시에 함께 보낼 contract deposit amount를 설정한다. 이는 gas랑은 전혀 상관없이 function이 payable
타입일 시 수령할 수 있는 목적의 amount이다.
Deploy
Contract 배포를 위한 준비 단계는 모두 완료되었다. 이제 배포할 contract를 선택하여 노란색 deploy 버튼을 눌러주면 environment EVM에 배포가 된다.
위 그림은 Remix VM(Merge) 버전에 배포한 결과이다. 빨간색 박스 안에서 배포한 결과를 확인할 수 있다. from
은 배포를 진행한 주체인 account이며, 화면 좌측의 account가 기존 100ether의 balance에서 99.999..874325ether로 gas fee만큼 사라진 것을 확인할 수 있다.
Contract가 배포 되었으면 contract address가 생성된다. 좌측 Deployed Contracts 항목에서 방금 배포한 contract의 주소를 0xD91...39138
처럼 확인할 수 있다. 이렇게 배포된 상태에서 remix를 이용하여 execute transaction을 전송하거나 state query를 진행할 수 있다.
Run transaction & Query
Deploy된 contract의 toggle 버튼을 누르면 배포한 contract를 컨트롤 할 수 있는 항목들이 확인된다. Transaction을 전송하여 state를 변경할 수 있는 function은 노란색으로, query를 할 수 있는 function은 파란색으로 확인되니 구분하기 쉬울 것이다.
Send tx
입력할 수 있는 uint256 내의 숫자를 선택하여 노란색 store 버튼을 누르면 EVM에 tx를 전송한다. 테스트로 17을 전송한다. 하단 콘솔창에서 확인되는 것처럼 tx send가 성공했다면 contract 내의 로직에 맞게 state 변경을 진행했을 것이다.
Query
입력한 state 변경 값을 확인하기 위해 파란색 retrieve 버튼을 누르면 입력되어 있는 값을 확인할 수 있다. 이전에 tx를 전송했던 것처럼 17이 retrieve 버튼 아래에 확인된다.
Conclusion
Solidity 개발을 위한 IDE인 Remix의 사용방법을 알아보았다. Remix는 ethereum smart contract의 시작부터 끝까지 모두 동작시킬 수 있는 멋진 IDE이다. Text editor, compile, deploy, execute까지 만능이다. 테스트 용도 뿐만 아니라 실제 상용환경으로도 배포가 가능하기 때문에 정말 다재다능한 IDE라 생각된다. Ethereum solidity 기반 dApp이 주류가 된 이유 중에 하나도 이 편리한 IDE 덕분이라 생각한다.
'Blockchain > Smart contract' 카테고리의 다른 글
Golang을 이용하여 Solidity smart contract 다루기 (2) | 2024.11.13 |
---|---|
CosmWasm 컨트랙트 개발 시 전략과 신경 쓰면 좋은 점 (1) | 2023.11.01 |
CosmWasm 기본 개념 및 구조 (0) | 2023.07.06 |
CosmWasm Contract Migration (0) | 2023.06.15 |
CosmWasm smart contract 배포하기 (0) | 2023.06.14 |