Building a Simple Token Tracker Using Web3 Data APIs

๋นŒ๋” ์—ฌ๋Ÿฌ๋ถ„ ์•ˆ๋…•ํ•˜์„ธ์š”! Token ํŠœํ† ๋ฆฌ์–ผ์— ์˜ค์‹  ๊ฒƒ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค ๐ŸŽ‰

์ด๋ฒˆ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ๊ฐ„๋‹จํ•œ Token Tracker๋ฅผ ์„ค๊ณ„ํ•˜๊ณ  ์ง์ ‘ ๋งŒ๋“ค์–ด๊ฐ€๋Š” ๊ณผ์ •์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ’ก

๋ณธ ํŠœํ† ๋ฆฌ์–ผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ˆœ์„œ๋กœ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค!

Step 1. ์š”๊ตฌ์‚ฌํ•ญ ์ˆ˜๋ฆฝ ๋ฐ ์—ฐ๋™ํ•  API ์‚ดํŽด๋ณด๊ธฐ

Step 2. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •ํ•˜๊ธฐ

Step 3. API ์—ฐ๋™ํ•˜๊ธฐ

Step 4. ์˜ˆ์ œ ์ฝ”๋“œ๋กœ ๋™์ž‘ ํ™•์ธํ•˜๊ธฐ

๊ทธ๋Ÿผ ํ•˜๋‚˜์”ฉ ์ง„ํ–‰ํ•ด๋ณผ๊นŒ์š”?


Step 1. ์š”๊ตฌ์‚ฌํ•ญ ์ˆ˜๋ฆฝ ๋ฐ ์—ฐ๋™ํ•  API ์‚ดํŽด๋ณด๊ธฐ

๊ตฌํ˜„ํ•  Token Tracker๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ฐ„๋‹จํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋„๋ก ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ๊ธฐ๋Šฅ 1. ๊ณ„์ • ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ํ•ด๋‹น ๊ณ„์ •์ด ๋ณด์œ ํ•˜๊ณ  ์žˆ๋Š” Token์˜ ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.
    • ๊ธฐ๋Šฅ 1-1. ๊ณ„์ • ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ํ•ด๋‹น ๊ณ„์ •์ด ๋ณด์œ ํ•˜๊ณ  ์žˆ๋Š” Native Token์˜ ์ˆ˜๋Ÿ‰์„ ๋ณด์—ฌ์ค€๋‹ค.
    • ๊ธฐ๋Šฅ 1-2. ๊ณ„์ • ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ํ•ด๋‹น ๊ณ„์ •์ด ๋ณด์œ ํ•˜๊ณ  ์žˆ๋Š” Token ๋ชฉ๋ก์„ ์กฐํšŒํ•˜์—ฌ ๋ณด์—ฌ์ค€๋‹ค.
แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2024-08-30 แ„‹แ…ฉแ„’แ…ฎ 5.19.11.png
  • ๊ธฐ๋Šฅ 2. Token ๋ชฉ๋ก์—์„œ ํŠน์ • Token์„ ํด๋ฆญํ•˜๋ฉด ํ•ด๋‹น Token์— ๋Œ€ํ•œ Metadata์™€ ํ•จ๊ป˜ ์•„๋ž˜ ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.
    • ๊ธฐ๋Šฅ 2-1. Token Contract์˜ Metadata๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๊ธฐ๋Šฅ 2-2. ํ•ด๋‹น Token์ด ๊ฑฐ๋ž˜๋œ ์ด๋ ฅ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2024-09-02 แ„‹แ…ฉแ„Œแ…ฅแ†ซ 11.00.30.png

๊ฐ„๋‹จํ•˜์ง€๋งŒ Token์™€ ๊ด€๋ จ๋œ ์ค‘์š”ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ๋ชจ๋‘ ๋‹ด๊ณ  ์žˆ๋„ค์š”! ์ด์ œ Nodit์˜ Web3 Data API ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๊ฐ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์–ด๋–ค API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณผ๊นŒ์š”?

๊ธฐ๋Šฅ 1-1. ๊ณ„์ • ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ํ•ด๋‹น ๊ณ„์ •์ด ๋ณด์œ ํ•˜๊ณ  ์žˆ๋Š” Native Token์˜ ์ˆ˜๋Ÿ‰์„ ๋ณด์—ฌ์ค€๋‹ค.

์ด ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ Input์— ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜๊ณ  [๊ฒ€์ƒ‰] ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์‘๋‹ต์œผ๋กœ๋ถ€ํ„ฐ ํ•ด๋‹น ๊ณ„์ •์ด ๋ณด์œ ํ•œ Native Token์˜ ์ˆ˜๋Ÿ‰์„ ์กฐํšŒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด ์—ฐ๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2024-08-30 แ„‹แ…ฉแ„’แ…ฎ 5.45.37.png

์ด๋Ÿฌํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” API๋กœ Web3 Data API์˜ Token API์ค‘ getNativeBalanceByAccount ๋ผ๋Š” API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒ ๋„ค์š”! ์•„๋ž˜์™€ ๊ฐ™์ด curl์„ ์ž‘์„ฑํ•˜์—ฌ API๋ฅผ ํ˜ธ์ถœํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

curl --request POST \
     --url https://web3.nodit.io/v1/ethereum/mainnet/native/getNativeBalanceByAccount \
     --header 'X-API-KEY: Input_your_API_Key' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "accountAddress": "Input_your_account_address"
}
'

์œ„ ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์‘๋‹ต์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. wei ๋‹จ์œ„๋กœ ์ˆ˜๋Ÿ‰์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{
  "ownerAddress": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
  "balance": "29435021161388550777"
}

๊ธฐ๋Šฅ 1-2. ๊ณ„์ • ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ํ•ด๋‹น ๊ณ„์ •์ด ๋ณด์œ ํ•˜๊ณ  ์žˆ๋Š” Token ๋ชฉ๋ก์„ ์กฐํšŒํ•˜์—ฌ ๋ณด์—ฌ์ค€๋‹ค.

๊ณ„์ •์ด ๋ณด์œ ํ•œ Native Token์˜ ์ˆ˜๋Ÿ‰ ์กฐํšŒ์™€ ๋™์ผํ•˜๊ฒŒ ์‚ฌ์šฉ์ž๊ฐ€ Input์— ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜๊ณ  [๊ฒ€์ƒ‰] ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์‘๋‹ต์œผ๋กœ๋ถ€ํ„ฐ ํ•ด๋‹น ๊ณ„์ •์ด ๋ณด์œ ํ•œ Token์˜ ๋ชฉ๋ก์„ ์กฐํšŒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2024-08-30 แ„‹แ…ฉแ„’แ…ฎ 5.54.28.png

Native Token์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋กœ์ง๊ณผ ๋™์ผํ•˜๋‹ˆ Native Token์„ ์กฐํšŒํ•œ ํ›„ Token ๋ชฉ๋ก์„ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๋Š” API๋ฅผ ํ˜ธ์ถœํ•ด ๋ชฉ๋ก์„ ๋ถˆ๋Ÿฌ์˜ค๋ฉด ๋˜๊ฒ ๋„ค์š”!

Nodit์—์„œ๋Š” getTokensOwnedByAccount ๋ผ๋Š” API๋กœ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์•„๋ž˜์™€ ๊ฐ™์ด curl์„ ์ž‘์„ฑํ•ด API๋ฅผ ํ˜ธ์ถœํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

curl --request POST \
     --url https://web3.nodit.io/v1/ethereum/mainnet/token/getTokensOwnedByAccount \
     --header 'X-API-KEY: Input_your_API_Key' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "accountAddress": "Input_your_account_address"
}
'

์œ„ ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์‘๋‹ต์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. balance์˜ ๊ฒฝ์šฐ, Wei ๋‹จ์œ„๋กœ ์ œ๊ณต๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

{
    "rpp": 20,
    "page": 2,
    "count": 389,
    "items": [
        {
            "ownerAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
            **"balance": "1000000000000000000000000",**
            "contract": {
                **"address": "0x6B1E3c59561d299Cab1eAa18bbD03eE3fFD2A0C7",**
                "deployedTransactionHash": "0x5eacfd6869aac83fd7b74fb1740d24d504daddcf90d61953b47703194a033af1",
                "deployedAt": "2023-10-26T16:49:59.000Z",
                "deployerAddress": "0x788A0E6c06448723D74efDe653E9d567faA262F5",
                "logoUrl": null,
                "type": "ERC20",
                **"name": "Tether",
                "symbol": "USDT",**
                "totalSupply": "1000000001000000000000000000",
                **"decimals": 18**
            }
        },
      ...
    ]
}

๊ธฐ๋Šฅ 2-1. Token Contract์˜ Metadata๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ๋ชฉ๋ก์˜ Token ์ค‘ ํ•œ ๊ฐ€์ง€๋ฅผ ์„ ํƒํ•˜๋ฉด Contract์˜ Metadata์™€ ๊ฑฐ๋ž˜ ์ด๋ ฅ์„ ๋ณด์—ฌ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ๋‹ค์ด์–ด๊ทธ๋žจ๊ณผ ๊ฐ™์ด ์—ฐ๋™ํ•  ์ˆ˜ ์žˆ๊ฒ ๋„ค์š”.

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2024-08-30 แ„‹แ…ฉแ„’แ…ฎ 6.05.05.png

Nodit์—์„œ ์ œ๊ณตํ•˜๋Š” getTokenContractMetadataByContracts API๋กœ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ contractAddress๋ฅผ ์ด์šฉํ•ด ์•„๋ž˜์™€ ๊ฐ™์ด API๋ฅผ ํ˜ธ์ถœํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 curl --request POST \
     --url https://web3.nodit.io/v1/ethereum/mainnet/token/getTokenContractMetadataByContracts \
     --header 'X-API-KEY: Input_your_API_Key' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "contractAddresses": [
    "Input_your_contract_address"
  ]
}
'

์œ„ ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ’์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ค‘์—์„œ ํ•„์š”ํ•œ ๊ฐ’์„ ์„ ํƒํ•ด ํ™”๋ฉด์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

[
  {
    "address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
    "deployedTransactionHash": "0x2f1c5c2b44f771e942a8506148e256f94f1a464babc938ae0690c6e34cd79190",
    "deployedAt": "2017-11-28T00:41:21.000Z",
    "deployerAddress": "0x36928500Bc1dCd7af6a2B4008875CC336b927D57",
    "logoUrl": "https://cdn.luniverse.io/img/crypto-currencies/ethereum/mainnet/0xdac17f958d2ee523a2206206994597c13d831ec7/64x64.png",
    "type": "ERC20",
    "name": "Tether USD",
    "symbol": "USDT",
    "totalSupply": "53981730120396390",
    "decimals": 6
  }
]

๊ธฐ๋Šฅ 2-2. ํ•ด๋‹น Token์ด ๊ฑฐ๋ž˜๋œ ์ด๋ ฅ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ ๋‹ค์Œ์€ ํŠน์ • Token์˜ ๊ฑฐ๋ž˜ ์ด๋ ฅ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ฐจ๋ก€ ์ž…๋‹ˆ๋‹ค. 2-1๊ณผ ๋™์ผํ•œ ํ˜ธ์ถœ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ ๊ฑฐ๋ž˜ ์ด๋ ฅ์— ๋Œ€ํ•œ ์ •๋ณด ๋ชฉ๋ก์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Nodit์—์„œ๋Š” getTokenTransfersByContract API๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ํ•ด๋‹น API๋ฅผ ์ด์šฉํ•ด ํŠน์ • Token์˜ ๊ฑฐ๋ž˜ ์ด๋ ฅ์„ ์กฐํšŒํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2024-08-30 แ„‹แ…ฉแ„’แ…ฎ 6.07.27.png
curl --request POST \
     --url https://web3.nodit.io/v1/ethereum/mainnet/token/getTokenTransfersByContract \
     --header 'X-API-KEY: Input_your_API_Key' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "contractAddress": "Input_your_contract_address",
}
'

์œ„ ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ’์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ค‘์—์„œ ํ•„์š”ํ•œ ๊ฐ’์„ ์„ ํƒํ•ด ํ™”๋ฉด์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{
  "rpp": 20,
  "cursor": "eyJzb3J0IjpbImJsb2NrX3RpbWVzdGFtcDpkZXNjIiwiYmxvY2tfbnVtYmVyOmRlc2MiLCJsb2dfaW5kZXg6ZGVzYyJdLCJzZWFyY2hBZnRlciI6WzE3MjUwMDg4MTkwMDAsMjA2NDAzOTEsMjAzXX0=",
  "items": [
    {
      "from": "0x0802Dd2EDD3f9faD39A9173B4595be819f201d61",
      "to": "0x09DEdfcef409fCaB410D79E2834FAD16554d1B7c",
      "value": "10200000",
      "timestamp": 1725008831,
      "blockNumber": 20640392,
      "transactionHash": "0x50793eedb9083a23d3725df8c31d727b0365e004dccf3f58ad9353c391c62088",
      "logIndex": 240,
      "contract": {
        "address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
        "deployedTransactionHash": "0x2f1c5c2b44f771e942a8506148e256f94f1a464babc938ae0690c6e34cd79190",
        "deployedAt": "2017-11-28T00:41:21.000Z",
        "deployerAddress": "0x36928500Bc1dCd7af6a2B4008875CC336b927D57",
        "logoUrl": "https://cdn.luniverse.io/img/crypto-currencies/ethereum/mainnet/0xdac17f958d2ee523a2206206994597c13d831ec7/64x64.png",
        "type": "ERC20",
        "name": "Tether USD",
        "symbol": "USDT",
        "totalSupply": "53981730120396390",
        "decimals": 6
      }
    },
  ...
  ]
}

Step 2. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •

๊ฐ„๋‹จํ•œ DApp ๊ตฌํ˜„์„ ์œ„ํ•ด ์•„๋ž˜ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๊ตฌ์„ฑ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์„ค์น˜๋ฅผ ์œ„ํ•œ ๋„์›€์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•ด์ฃผ์„ธ์š”.

VS Code Required

์†Œ์Šค ์ฝ”๋“œ ํŽธ์ง‘๊ธฐ๋กœ ์ฝ”๋“œ๋ฅผ ์†์‰ฝ๊ฒŒ ์ž‘์„ฑํ•˜๊ณ  ํŽธ์ง‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Node.js Required

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ์œผ๋กœ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰์„ ์œ„ํ•ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

git Optional

๋ถ„์‚ฐ ๋ฒ„์ „ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์œผ๋กœ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๋กœ์ปฌ ํ™˜๊ฒฝ์— ๋‹ค์šด๋กœ๋“œ ๋ฐ›๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ์™ธ์— ์‚ฌ์šฉ๋œ ํ”„๋ ˆ์ž„์›Œํฌ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” Step 4. ์˜ˆ์ œ์ฝ”๋“œ์˜ package.json ํŒŒ์ผ์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

Step 3. API ์—ฐ๋™

์ด๋ฏธ ํ”„๋กœ์ ํŠธ ๊ตฌํ˜„์— ํ•„์š”ํ•œ API๋“ค์„ ์‚ดํŽด๋ณด๊ณ  ํ˜ธ์ถœ ์‹œ ์‘๋‹ต์ด ์–ด๋–ป๊ฒŒ ์˜ค๋Š”์ง€ ํ™•์ธํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ด API๋“ค์„ ์ด์šฉํ•ด ํ”„๋ก ํŠธ์—”๋“œ ์ฝ”๋“œ์— ์—ฐ๋™ํ•ด ๋ณผ๊นŒ์š”?

๐Ÿ“˜

Nodit API Key๋ฅผ ๋ฐœ๊ธ‰ ๋ฐ›์œผ์…จ๋‚˜์š”?

Nodit API๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Nodit API Key๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์•„์ง Nodit์„ ์‹œ์ž‘ํ•˜์ง€ ์•Š์œผ์…จ๋‹ค๋ฉด ์•„๋ž˜ ๋งํฌ๋œ ํŽ˜์ด์ง€์˜ ์•ˆ๋‚ด์— ๋”ฐ๋ผ Nodit ์ฝ˜์†”์— ๊ฐ€์ž…ํ•˜๊ณ , ๊ฐœ๋ฐœ ์—ฐ๋™์„ ์œ„ํ•œ API Key๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์•„๋ณด์„ธ์š”. ์‹œ์ž‘์€ ๋ฌด๋ฃŒ์ž…๋‹ˆ๋‹ค!

๐Ÿ‘‰ย Nodit Node Quickstart

์šฐ์„  Nodit์˜ API Key์™€ Protocol, Network๋ฅผ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์„ค์ •ํ•˜์—ฌ ํ•˜๋‚˜์˜ ํŒŒ์ผ์—์„œ ๊ด€๋ฆฌํ•˜๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

// .env

VITE_API_KEY=Your_Nodit_API_Key
VITE_PROTOCOL=ethereum
VITE_NETWORK=mainnet

ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ํŒŒ์ผ์— ์ž‘์„ฑํ•œ ๊ฐ’์„ ๋ถˆ๋Ÿฌ์™€ HTTP ํ†ต์‹ ์„ ์ด์šฉํ•ด API๋ฅผ ํ˜ธ์ถœํ•  ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

// instance.ts

import axios, { AxiosInstance } from "axios";

const apiKey = import.meta.env.VITE_API_KEY;
const protocol = import.meta.env.VITE_PROTOCOL;
const network = import.meta.env.VITE_NETWORK;

export function createWeb3ApiInstance(): AxiosInstance {
  const instance = axios.create({
    baseURL: `https://web3.nodit.io/v1/${protocol}/${network}`,
    headers: {
      "X-API-KEY": `${apiKey}`,
      "Content-Type": "application/json",
      Accept: "application/json",
    },
  });
  return instance;
}

์•ž์„œ ์ž‘์„ฑํ•œ ์ธ์Šคํ„ด์Šค๋ฅผ ์ด์šฉํ•ด API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“  ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

// useQueries.ts

import { useQuery } from "@tanstack/react-query";
import { createWeb3ApiInstance } from "./instance";
const instance = createWeb3ApiInstance();

export const useGetTokensOwnedByAccount = (
  accountAddress: string,
  page: number,
  rpp = 20
) => {
  return useQuery({
    queryKey: ["getTokensOwnedByAccount", accountAddress, page, rpp],
    queryFn: async () =>
      instance.post("token/getTokensOwnedByAccount", {
        accountAddress,
        withCount: true,
        rpp,
        page,
      }),
    retry: false,
    staleTime: 1000 * 60 * 5,
  });
};

ํ™”๋ฉด์— ๋…ธ์ถœ๋  ํ…Œ์ด๋ธ” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ฒ˜๋ฆฌํ•œ ๋ฐ์ดํ„ฐ์ธ ownedTokenByAccountData๋ฅผ ๋ฐ›์•„ ํ…Œ์ด๋ธ” ์š”์†Œ์— ๋ฐ”์ธ๋”ฉ ํ•ฉ๋‹ˆ๋‹ค.

//TokenTable.tsx

import {
  TokenTableProps,
  TokensOwnedByAccountResponse,
} from "./interface";

const TokenTable = ({ ownedTokensByAccountData }: TokenTableProps) => {

  return (
    <div className="flex flex-col items-center justify-center">
      <div className="mt-10 text-2xl font-bold">Token List</div>
      {ownedTokenByAccountData && ownedTokenByAccountData.length > 0 ? (
          <table className="w-full max-w-7xl table-fixed border-collapse mt-5 mb-10 shadow-xl shadow-black ">
            <thead>
              <tr className="border-2 border-noditGreen bg-noditGreen text-white ">
                <th className="p-5">Number</th>
                <th className="p-5">Name</th>
                <th className="p-5">Symbol</th>
                <th className="p-5">Decimals</th>
                <th className="p-5">Balances</th>
              </tr>
            </thead>
            <tbody>
              {ownedTokenByAccountData.map(
                (item: TokensOwnedByAccountResponse, index: number) => (
                  <tr
                    key={item.contract.deployedTransactionHash}
                    className="border border-noditGreen hover:scale-105 duration-100 cursor-pointer "
                  >
                    <th className="font-bold p-5">
                      {ownedTokenByAccountData.page === 1
                        ? index + 1
                        : ownedTokenByAccountData.page * 20 + index - 19}
                    </th>
                    <th className="font-light p-5">{item.contract.name}</th>
                    <th className="font-light p-5">{item.contract.symbol}</th>
                    <th className="font-light p-5">{item.contract.decimals}</th>
                    <th className="font-light p-5">{item.balance}</th>
                  </tr>
                )
              )}
            </tbody>
          </table>
      ) : (
        <div>This Account doesn't have any Tokens</div>
      )}
    </div>
  );
};

export default TokenTable;

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” API์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ํ…Œ์ด๋ธ” ์ปดํฌ๋„ŒํŠธ์— API ํ˜ธ์ถœ ๊ฒฐ๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

// TokenList.tsx

import React from "react";
import {
  useGetTokensOwnedByAccount,
} from "./useQueries";
import { useParams } from "react-router-dom";
import TokenTable from "./components/TokenTable";

const TokenList = (): React.ReactElement => {
  const { accountAddress } = useParams();
  if (!accountAddress) throw new Error("Check your account address");
  const {
    isError,
    data: getTokensOwnedByAccountData,
    isLoading,
  } = useGetTokensOwnedByAccount(accountAddress, currentPage);

  if (isLoading) return <div>Loading...</div>;

  if (isError) return <div>You have to connect node</div>;

  return (
    <div>
      <TokenTable
        ownedTokensByAccountData={getTokensOwnedByAccountData?.data}
      />
    </div>
  );
};

export default TokenList;

์œ„์™€ ๊ฐ™์ด ์ž‘์„ฑ ํ›„ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์‚ฌ์ง„๊ณผ ๊ฐ™์ด ๋ฐ์ดํ„ฐ๋ฅผ ํ™”๋ฉด์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2024-08-30 แ„‹แ…ฉแ„’แ…ฎ 6.55.39.png

Step 4. ์˜ˆ์ œ ์ฝ”๋“œ

์œ„์—์„œ ๊ตฌํ˜„ํ•œ ๋‚ด์šฉ์„ ํฌํ•จํ•œ, ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด๋†“์€ ์˜ˆ์ œ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ณต์œ ํ•ด ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

  • ์†Œ์Šค ์ฝ”๋“œ ๋‹ค์šด๋กœ๋“œ ๋ฐ ์ ‘๊ทผ
$ git clone https://github.com/Lambda256/Nodit-EVM-Tutorials
$ cd Nodit-EVM-Tutorials
$ cd token_tutorial
  • ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐํ™” ๋ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜
$ npm install

์‚ฌ์šฉ์ž๊ฐ€ ์ด์šฉํ•  ํ”„๋กœํ† ์ฝœ๊ณผ ๋„คํŠธ์›Œํฌ ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ์ž์˜ Nodit API Key๋ฅผ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. Nodit API Key์™€ ํ”„๋กœํ† ์ฝœ, ๋„คํŠธ์›Œํฌ์˜ ๊ฒฝ์šฐ Nodit Console์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ธฐ๋ณธ ๊ฐ’์œผ๋กœ ์ž…๋ ฅ๋˜์–ด ์žˆ๋Š” ethereum๊ณผ mainnet ์™ธ์—๋„ ์‚ฌ์šฉ์ž๊ฐ€ ์—ฐ๊ฒฐํ•œ ๋…ธ๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// .env

VITE_API_KEY=Your_Nodit_API_Key
VITE_PROTOCOL=ethereum
VITE_NETWORK=mainnet

ํ™˜๊ฒฝ ์„ค์ •์„ ์™„๋ฃŒํ•œ ํ›„ ํ„ฐ๋ฏธ๋„์— ์•„๋ž˜์˜ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์—ฌ Nodit Token Tutorial์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

$ npm run dev
แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2024-08-30 แ„‹แ…ฉแ„’แ…ฎ 6.56.52.png

Congratulation! ๐ŸŽŠ

Nodit Token Tutorial์„ ๋ชจ๋‘ ์™„๋ฃŒํ•˜์˜€์Šต๋‹ˆ๋‹ค! ๋‹ค๋ฅธ Nodit API๋ฅผ ์ด์šฉํ•˜์—ฌ ๋‚˜๋งŒ์˜ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด ๋ณด์„ธ์š”!

๋”์šฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ Nodit์˜ API๊ฐ€ ๊ถ๊ธˆํ•˜์‹ ๊ฐ€์š”? ์•„๋ž˜ ๋งํฌ๋ฅผ ๋ˆŒ๋Ÿฌ ํ™•์ธํ•ด ๋ณด์„ธ์š”!

Nodit API Reference ๋ฐ”๋กœ๊ฐ€๊ธฐ