Blog About
Table of Contents
  • การทำงานของโปรแกรม
  • เครื่องมือที่ใช้

Binance Smart Chain + Ethereun Chain ส่องเหรียญ BNB และ ETH

Apisit N.
30 Sep 2021

ในตอนนี้จะลองเขียนโปรแกรมส่องเหรียญหลักของทั้ง Ethereum chain และ Binance Smart Chain โดยใช้โค้ดเหมือนกันเปลี่ยนแค่ค่าบางอย่างที่ใช้ในการเชื่อมต่อ blockchain

คำว่าเหรียญหลักในที่นี้ผมหมายถึง เหรียญหรือโทเคนที่ใช้เป็นค่าธรรมเนียม(gas)ในระบบ

การทำงานของโปรแกรม

การทำงานของโปรแกรม

  1. รับค่าหรือกำหนดค่าของ wallet address ที่ต้องการจะดู balances
  2. สร้างการเชื่อมต่อไปยัง node ของ blockchain ที่ต้องการ
  3. ส่งคำสั่งร้องขอดูจำนวนเหรียญที่มีของ wallet address นั้นๆ

จากขั้นตอนนี้เรารู้จำนวนเหรียญแล้ว แล้วเหรียญที่เรามีมันมีมูลค่าเท่าไหร่หล่ะ

ก่อนจะไปหาราคาเราลองมาดูสูตรกันก่อนว่าคำนวณยังไง

จำนวนเหรียญ * ราคา = มูลค่า

สิ่งที่เรามีอยู่แล้วคือคือ จำนวนเหรียญ ส่วนของราคาในแต่ละตลาดราคาก็ไม่ตรงกัน สิ่งที่เราจะทำคือนำราคาแต่ละตลาดมาหาค่าเฉลี่ยเพื่อให้ได้ค่าตรงกลางเช่น

  • exchange A: 1 BNB = 342 USDT
  • exchange A: 1 BNB = 344 BUSD
  • exchange B: 1 BNB = 350 USDT
  • exchange C: 1 BNB = 356 USDT
  • exchange D: 1 BNB = 350 USDT
  • exchange E: 1 BNB = 450 USDT
  • exchange E: 1 BNB = 456 BUSD
  • (342 + 344 + 350 + 356 + 350 + 450 + 456) / 7 = 378.28
  • (342 + 344 + 350 + 356 + 350) / 5 = 348.4

จะเห็นได้ว่า exchange E จะมีราคาที่แตกต่างจากพวกมาก ๆ เราไม่ควรนำมาคำนวณนะครับ เพราะจะทำให้ราคาเฉลี่ยเกินกว่าความเป็นจริง

เมื่อเรามีครบแล้วทั้ง จำนวนเหรียญ และ ราคา เราก็สามารถคำนวณมูลค่าได้แล้วครับ ส่องเหรียญ BNB และ ETH https://deefu.netlify.app/

เครื่องมือที่ใช้

  • react
  • axios
  • coingecko-api
  • styled-components
  • web3
src/assets/network.js
1
module.exports = {
2
network: {
3
eth: {
4
mainnet: "https://mainnet.infura.io/v3/apikey",
5
ropsten: "https://ropsten.infura.io/v3/apikey",
6
kovan: "https://kovan.infura.io/v3/apikey",
7
rinkeby: "https://rinkeby.infura.io/v3/apikey",
8
ws: {
9
kovan: "wss://kovan.infura.io/ws/v3/apikey",
10
},
11
},
12
polygon: {
13
mainnet:
14
"https://polygon-mainnet.infura.io/v3/apikey",
15
},
16
bsc: {
17
mainnet: "https://bsc-dataseed1.binance.org:443",
18
testnet: "https://data-seed-prebsc-1-s1.binance.org:8545",
19
},
20
},
21
};
src/App.js
1
import { useState, useEffect } from "react";
2
import "./App.css";
3
import styled from "styled-components";
4
5
import Web3 from "web3";
6
import axios from "axios";
7
8
import { network } from "./assets/network";
9
10
async function coins_fetch(id) {
11
if (id.length > 0) {
12
let result = await axios.get(
13
`https://api.coingecko.com/api/v3/coins/${id}`,
14
{
15
localization: "false",
16
tickers: "true",
17
market_data: "false",
18
community_data: "false",
19
developer_data: "false",
20
sparkline: "false",
21
}
22
);
23
// console.log(result);
24
return result;
25
}
26
}
27
28
async function fetch_token_price(tokenid) {
29
try {
30
let coin_price = await coins_fetch(tokenid);
31
32
let price_multi_market = coin_price.data.tickers.filter(
33
(item) =>
34
item.is_stale == false &&
35
(item.target == "USDT" || item.target == "USDC")
36
);
37
let token_price_average = price_multi_market.map((item) => item.last);
38
if (token_price_average.length > 0) {
39
token_price_average =
40
token_price_average.reduce((prev, next) => prev + next) /
41
price_multi_market.length;
42
return {
43
image: coin_price.data.image,
44
price: token_price_average,
45
};
46
} else {
47
// colsole.log(tokenid);
48
}
49
} catch (error) {
50
console.log("err: " + tokenid);
51
}
52
}
53
54
async function main1(data) {
55
let My_Portfolio = [];
56
57
let options = {
58
chain: "",
59
wallet: "",
60
};
61
options = { ...options, ...data };
62
let { wallet } = options;
63
64
let network_node = "";
65
let Currency_Symbol = "BNB";
66
let Curreny_Id = "binancecoin";
67
if (options.chain.toLocaleLowerCase() == "bsc") {
68
network_node = network.bsc.mainnet;
69
Currency_Symbol = "BNB";
70
Curreny_Id = "binancecoin";
71
} else if (options.chain.toLocaleLowerCase() == "eth") {
72
network_node = network.eth.mainnet;
73
Currency_Symbol = "ETH";
74
Curreny_Id = "ethereum";
75
}
76
77
// const web3 = new Web3(options.chain);
78
let web3 = new Web3(new Web3.providers.HttpProvider(network_node));
79
// let web3 = new Web3(new Web3.providers.HttpProvider(network.bsc.mainnet));
80
// web3 = new Web3(new Web3.providers.HttpProvider(network.eth.mainnet));
81
82
let Currency_Symbol_Balance = await web3.eth.getBalance(wallet);
83
const Currency_Symbol_balance_format = web3.utils.fromWei(
84
Currency_Symbol_Balance
85
);
86
87
My_Portfolio = {
88
symbol: Currency_Symbol,
89
balances: parseFloat(Currency_Symbol_balance_format).toFixed(3),
90
};
91
92
let result = await fetch_token_price(Curreny_Id);
93
if (result?.price !== undefined) {
94
My_Portfolio = {
95
...My_Portfolio,
96
image: result.image,
97
price: parseFloat(result.price).toFixed(2),
98
value: (My_Portfolio.balances * result.price).toFixed(3),
99
};
100
}
101
return My_Portfolio;
102
}
103
104
function App() {
105
const [input, setInput] = useState();
106
const [portfolio, setPortfolio] = useState();
107
const [isFetching, setIsFetching] = useState(false);
108
109
useEffect(() => {
110
(async () => {
111
if (input?.wallet?.length > 0) {
112
setIsFetching(true);
113
console.log("Loading... event 1");
114
let result_1 = await main1({ chain: "bsc", wallet: input.wallet });
115
let result_2 = await main1({ chain: "eth", wallet: input.wallet });
116
117
let arr = [result_1, result_2];
118
setPortfolio(arr);
119
120
console.log("done event 1");
121
setIsFetching(false);
122
}
123
})();
124
}, [input?.wallet]);
125
126
useEffect(() => {
127
if (isFetching == false) {
128
console.log("done");
129
}
130
}, [isFetching]);
131
132
const handleKeyPress = (event) => {
133
if (event.key === "Enter") {
134
let value = event.target.value;
135
setInput({ wallet: value });
136
}
137
};
138
139
return (
140
<div>
141
<Nav_Flex>
142
<Nav_Flex_Left>
143
{/* <div><img src={logo} /></div> */}
144
<div>
145
<a>Deefu</a>
146
</div>
147
</Nav_Flex_Left>
148
<Nav_Flex_Right>{/* <div><a>Home</a></div> */}</Nav_Flex_Right>
149
<div>
150
<Wrap_InputBox_Input
151
name="address"
152
placeholder="Search by Address"
153
onKeyPress={handleKeyPress}
154
/>
155
</div>
156
</Nav_Flex>
157
<Blog_Size>
158
<Title_Table>Wallet</Title_Table>
159
<Table>
160
<thead>
161
<tr>
162
<th>assets</th>
163
<th>balances</th>
164
<th>price</th>
165
<th>value</th>
166
</tr>
167
</thead>
168
<tbody>
169
{portfolio &&
170
portfolio
171
.sort(function (a, b) {
172
return a.value - b.value;
173
})
174
.map((item, index) => {
175
if (!item.price) {
176
return;
177
}
178
return (
179
<tr key={index}>
180
<td>
181
<Wrap_Symbol>
182
<Size_Symbol>
183
<Size_Symbol_Img src={item.image?.thumb} />
184
</Size_Symbol>
185
<Symbol_Name>{item.symbol}</Symbol_Name>
186
</Wrap_Symbol>
187
</td>
188
<td>{item.balances}</td>
189
<td>{item.price}</td>
190
<td>{item.value}</td>
191
</tr>
192
);
193
})}
194
</tbody>
195
</Table>
196
</Blog_Size>
197
<footer>
198
<span>v1.1.0</span>
199
<span> BSC + ETH</span>
200
</footer>
201
</div>
202
);
203
}
204
205
const Nav_Flex = styled.div`
206
display: flex;
207
justify-content: space-between;
208
align-items: center;
209
padding: 0 16px;
210
height: 64px;
211
box-shadow: rgb(20 100 249) 0px -4px 8px;
212
`;
213
const Nav_Flex_Left = styled.div`
214
display: flex;
215
justify-content: center;
216
align-items: center;
217
& a {
218
font-weight: 700;
219
color: rgb(0, 0, 0);
220
text-decoration: none;
221
transition: all 0.25s ease 0s;
222
margin-left: 0px;
223
padding-bottom: 5px;
224
}
225
`;
226
const Nav_Flex_Right = styled.div`
227
display: flex;
228
justify-content: center;
229
align-items: center;
230
& a {
231
font-weight: 700;
232
color: rgb(0, 0, 0);
233
text-decoration: none;
234
transition: all 0.25s ease 0s;
235
margin-left: 0px;
236
padding-bottom: 5px;
237
}
238
`;
239
const Blog_Size = styled.div`
240
box-sizing: border-box;
241
margin: 0 auto;
242
padding: 8px;
243
min-width: 0px;
244
width: 100%;
245
max-width: 960px;
246
min-height: 100vh;
247
position: relative;
248
`;
249
const Wrap_InputBox_Input = styled.input`
250
width: 300px;
251
height: 1.6em;
252
padding: 6px 12px;
253
border-radius: 40px;
254
border: 1px solid rgba(0, 0, 0, 0.25);
255
`;
256
const Title_Table = styled.h4`
257
padding: 0 1rem;
258
margin: 0;
259
`;
260
const Table = styled.table`
261
width: 100%;
262
display: table;
263
border-spacing: 0;
264
border-collapse: collapse;
265
& thead {
266
display: table-header-group;
267
}
268
& thead tr {
269
color: inherit;
270
display: table-row;
271
outline: 0;
272
vertical-align: middle;
273
}
274
& thead tr th {
275
font-size: 16px;
276
border-bottom: none;
277
278
padding: 0.75rem 1rem;
279
white-space: nowrap;
280
281
color: #2e2e2e;
282
line-height: 1.5rem;
283
284
text-align: left;
285
}
286
& tbody {
287
display: table-header-group;
288
}
289
& tbody tr {
290
color: inherit;
291
display: table-row;
292
outline: 0;
293
vertical-align: middle;
294
}
295
& tbody tr td {
296
font-size: 16px;
297
border-bottom: none;
298
299
padding: 0.75rem 1rem;
300
white-space: nowrap;
301
302
color: #2e2e2e;
303
line-height: 1.5rem;
304
305
text-align: left;
306
}
307
`;
308
const Wrap_Symbol = styled.div`
309
display: inline-grid;
310
gap: 8px;
311
align-items: center;
312
grid-auto-flow: column;
313
justify-content: flex-start;
314
`;
315
const Size_Symbol = styled.div`
316
display: flex;
317
position: relative;
318
font-size: 22px;
319
align-items: center;
320
margin-right: 0px;
321
`;
322
const Size_Symbol_Img = styled.img`
323
object-fit: cover;
324
object-position: center center;
325
flex-shrink: 0;
326
border-radius: 50%;
327
padding: 1px;
328
background: white;
329
330
width: 22px;
331
height: 22px;
332
`;
333
const Symbol_Name = styled.p`
334
color: #2e2e2e;
335
336
font-size: 14px;
337
line-height: 1.43;
338
`;
339
340
export default App;

สุดท้าย ก็หวังว่าบทความนี้จะเป็นประโยชน์กับใครหลาย ๆ คนนะครับ

© 2025 Apisit N.