이 글에서는 solidity 문법을 다루지는 않으며, 배포된 solidity의 Contract 함수를 web3에서 호출하는 방법을 포스팅합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// SPDX-License-Identifier GPL-30
pragma solidity >= 0.7.0 < 0.9.0;
 
contract Hello {
    uint public num = 0;
    string public hi = "hello solidity!!";
 
    function addNumAndX(uint x) public {
        num += x;
    }
 
    function hello() public view returns(string memory) {
        return hi;
    }
 
    function getNum() public view returns(uint) {
        return num;
    }
}
 
cs

우선 solidity를 이렇게 작성해줍니다. 저는 Remix에서 작성하였습니다.

그 다음 compiler에서 컴파일을 해준 후에

 

deploy에서 배포를 해야합니다.

아래의 Deploy 버튼을 누르기 전, 환경을 "Injected Provider - MetaMask"로 변경합니다.

그리고 Deploy 버튼을 누릅니다.

 

그러면 해당 Contract를 배포하기 위한 가스비를 지불하는 창이 뜹니다.

위 창은 크롬의 MetaMask 확장 프로그램입니다.

 

배포가 완료되면 아래의 Deployed Contracts에 해당 컨트랙트가 뜹니다.

이제 우측 복사 버튼을 눌러 컨트랙트 주소를 가져올 수 있습니다.

이제 React로 넘어갑니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import {Component} from 'react';
import Web3 from 'web3';
 
class App extends Component {
    constructor(props) {
        super(props);
 
        const contractAddress = '0xaFA55F3E3DB63C536581762ceb3149aD3349913d';
        const abi = [
            {
                "inputs": [
                    {
                        "internalType""uint256",
                        "name""x",
                        "type""uint256"
                    }
                ],
                "name""addNumAndX",
                "outputs": [],
                "stateMutability""nonpayable",
                "type""function"
            },
            {
                "inputs": [],
                "name""getNum",
                "outputs": [
                    {
                        "internalType""uint256",
                        "name""",
                        "type""uint256"
                    }
                ],
                "stateMutability""view",
                "type""function"
            },
            {
                "inputs": [],
                "name""hello",
                "outputs": [
                    {
                        "internalType""string",
                        "name""",
                        "type""string"
                    }
                ],
                "stateMutability""view",
                "type""function"
            },
            {
                "inputs": [],
                "name""hi",
                "outputs": [
                    {
                        "internalType""string",
                        "name""",
                        "type""string"
                    }
                ],
                "stateMutability""view",
                "type""function"
            },
            {
                "inputs": [],
                "name""num",
                "outputs": [
                    {
                        "internalType""uint256",
                        "name""",
                        "type""uint256"
                    }
                ],
                "stateMutability""view",
                "type""function"
            }
        ];
 
        this.fromAddress = '0xE8Ed81Ae938Ede438501d12d87324AB1428A251a';
        this.web3 = new Web3(Web3.givenProvider || 'ws://some.local-or-remote.node:8546');
        this.contract = new this.web3.eth.Contract(abi, contractAddress);
    }
}
cs

web3 모듈을 가져와서 사용하고, abi, contractAddress를 이용하여 contract를 가져옵니다.

여기서 abi는 Contract 함수와 매개변수들의 정보가 담겨있는 JSON 형식의 리스트입니다.

Remix의 compiler 메뉴 밑에 해당 컨트랙트의 ABI를 복사할 수 있습니다.

그대로 가져와서 React 코드에 붙여줍니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
render() {
    return (
        <div className="App">
            <button onClick={this.connectWallet.bind(this)}>connectWallet</button>
            <br/>
            <button onClick={this.hello.bind(this)}>hello</button>
            <br/>
            <button onClick={this.addNumAndX.bind(this)}>addNumAndX</button>
            <br/>
            <button onClick={this.getNum.bind(this)}>getNum</button>
            <br/>
        </div>
    );
}
 
cs

화면에는 버튼을 놓았습니다.

각각 connectWallet, hello, addNumAndX, getNum 함수를 호출하는 버튼입니다.

함수에서는 constructor의 this를 사용할 수 없어 bind(this)로 this를 사용할 수 있도록 넘겨주었습니다.

 

이제 메소드입니다.

1
2
3
4
5
6
connectWallet() {
    this.web3.eth.requestAccounts().then((res) => {
        console.log(res);
    });
}
 
cs

지갑연결을 요청하는 코드입니다.

MetaMask가 설치되어있다면 문제없이 작동됩니다.

res는 지갑 주소가 array 형식으로 담겨있습니다.

 

1
2
3
4
5
6
hello() {
    this.contract.methods.hello().call((err, res) => {
        console.log(res);
   });
}
 
cs

다음은 Contract의 hello 함수를 호출하는 부분입니다.

string을 return받는 역할만 있어 가스비를 지출하지 않으며, call을 이용하여 호출할 수 있습니다.

res는 string입니다.

 

1
2
3
4
5
6
getNum() {
    this.contract.methods.getNum().call((err, res) => {
        console.log(res);
   });
}
 
cs

다음은 변수 num의 값을 가져오기 위한 getNum 함수 호출입니다.

hello 함수와 동일하게 call을 이용하여 호출합니다.

res는 uint입니다.

 

1
2
3
4
5
6
7
8
9
10
11
addNumAndX() {
    let x = 2;
    this.contract.methods.addNumAndX(x).send({fromthis.fromAddress})
        .on('receipt'function (res) {
            console.log(res);
        })
        .on('error'function (err) {
            console.log(err);
        });
}
 
cs

다음은 num에 x만큼 더하는 로직이 있는 addNumAndX 함수 호출입니다.

다른 함수와는 다르게 값을 변경하는 로직이 포함돼서 그런지 가스비를 요구하고 있습니다.

이 코드에서는 지불할 ETH가 0이지만 형식적으로 지불은 해야합니다.

가스비 지불을 위해 call 대신 send를 사용하였으며, 지불할 주소를 from에 작성하면 호출할 수 있습니다.

 

'receipt'을 감지하면 가스비 지불이 완료되었고, 트랜잭션 내역을 확인할 수 있습니다.

transactionHash를 통해 etherscan에서도 확인이 가능합니다.

그리고 getNum 함수를 다시 호출하면 값이 변경된 것을 확인할 수 있습니다.

 

다른 로직들은 [web3 공식문서]에서 확인해주시면 됩니다.

 

아래는 전체 코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import {Component} from 'react';
import Web3 from 'web3';
 
class App extends Component {
    constructor(props) {
        super(props);
 
        const contractAddress = '0xaFA55F3E3DB63C536581762ceb3149aD3349913d';
        const abi = [
            {
                "inputs": [
                    {
                        "internalType""uint256",
                        "name""x",
                        "type""uint256"
                    }
                ],
                "name""addNumAndX",
                "outputs": [],
                "stateMutability""nonpayable",
                "type""function"
            },
            {
                "inputs": [],
                "name""getNum",
                "outputs": [
                    {
                        "internalType""uint256",
                        "name""",
                        "type""uint256"
                    }
                ],
                "stateMutability""view",
                "type""function"
            },
            {
                "inputs": [],
                "name""hello",
                "outputs": [
                    {
                        "internalType""string",
                        "name""",
                        "type""string"
                    }
                ],
                "stateMutability""view",
                "type""function"
            },
            {
                "inputs": [],
                "name""hi",
                "outputs": [
                    {
                        "internalType""string",
                        "name""",
                        "type""string"
                    }
                ],
                "stateMutability""view",
                "type""function"
            },
            {
                "inputs": [],
                "name""num",
                "outputs": [
                    {
                        "internalType""uint256",
                        "name""",
                        "type""uint256"
                    }
                ],
                "stateMutability""view",
                "type""function"
            }
        ];
 
        this.web3 = new Web3(Web3.givenProvider || 'ws://some.local-or-remote.node:8546');
        this.fromAddress = '0xE8Ed81Ae938Ede438501d12d87324AB1428A251a';
        this.contract = new this.web3.eth.Contract(abi, contractAddress);
 
    }
 
    connectWallet() {
        this.web3.eth.requestAccounts().then((res) => {
            console.log(res);
        });
    }
 
    hello() {
        this.contract.methods.hello().call((err, res) => {
            console.log(res);
        });
    }
 
    addNumAndX() {
        let x = 2;
        this.contract.methods.addNumAndX(x).send({fromthis.fromAddress})
            .on('receipt'function (res) {
                console.log(res);
            })
            .on('error'function (err) {
                console.log(err);
            });
    }
 
    getNum() {
        this.contract.methods.getNum().call((err, res) => {
            console.log(res);
        });
    }
 
    render() {
        return (
            <div className="App">
                <button onClick={this.connectWallet.bind(this)}>connectWallet</button>
                <br/>
                <button onClick={this.hello.bind(this)}>hello</button>
                <br/>
                <button onClick={this.addNumAndX.bind(this)}>addNumAndX</button>
                <br/>
                <button onClick={this.getNum.bind(this)}>getNum</button>
                <br/>
            </div>
        );
    }
}
 
export default App;
 
cs

'Etc' 카테고리의 다른 글

AWS S3로 React 배포하기  (0) 2023.02.02
GitHub Actions를 이용한 Slack Notification  (0) 2023.01.30
GitLab -> GitHub Mirroring  (0) 2022.06.12
Unity Script로 카메라 Culling Mask 기능 사용하기  (2) 2021.10.29
Unity 카메라 Culling Mask  (0) 2021.10.29

+ Recent posts