[chainlink functions] Return Custom Data Types

채동기·2023년 6월 13일
0

Oracle

목록 보기
13/14

Cryptocompare GET /data/pricemultifull API를 호출하기 위해 Decentralized Oracle Network에 요청을 보냅니다. OCR은 오프체인 계산과 집계를 완료한 후 스마트 계약에 여러 응답을 반환합니다. 응답에는 자산 가격, 일일 거래량 및 시장 이름이 포함됩니다. 이 예제에서는 쿼리 매개변수를 사용하여 ETH/USD 자산 쌍을 지정하지만, HTTP 쿼리 매개변수를 구성하여 다른 자산에 대한 요청을 할 수 있습니다.

시작하기 전에
사전 준비
링크에 있는 내용을 해보고, 준비하셔야 지금 페이지의 내용을 따라 올 수 있습니다.

Chainlink Functions Starter Kit의 tutorials 브랜치로 깃 체크아웃해야합니다.

git checkout tutorials

예제는 /tutorials/3-custom-response directory 에 위치해 있습니다.

튜토리얼

이 튜토리얼은 ETH/USD, 일일 거래량 및 시장을 한 번의 요청으로 가져오도록 구성되어 있습니다. 코드 예제에 대한 자세한 설명을 보려면 설명 섹션을 참조하세요.

  1. config.js 파일을 엽니다. args 값이 ["ETH", "USD"]로 설정되어 있는 것을 확인합니다. 이는 현재 ETH/USD 가격과 일일 거래량을 가져오기를 원하는 것을 나타냅니다. args를 조정하여 지원되는 심볼의 목록을 가져올 수 있습니다. 자세한 내용은 API 문서를 참조하십시오. 이 예제에서 구성에 대한 자세한 설명은 요청 구성 설명 섹션을 참조하세요.

  2. source.js 파일을 열어 JavaScript 소스 코드를 분석합니다. 소스 파일 작성 방식에 대한 자세한 설명은 소스 코드 설명 섹션을 참조하십시오.

시뮬레이션

Chainlink Functions Hardhat 스타터 킷에는 로컬 환경에서 Functions 코드를 테스트하기 위한 시뮬레이터가 포함되어 있습니다. functions-simulate 명령을 사용하면 코드를 로컬 런타임 환경에서 실행하고 엔드 투 엔드 이행을 시뮬레이션할 수 있습니다. 시뮬레이션은 Decentralized Oracle Network에 요청을 제출하기 전에 문제를 해결하는 데 도움이 될 수 있습니다.

로컬에서 소스 코드를 실행하고 config.js와 Functions-request-source.js 파일이 올바르게 작성되었는지 확인하기 위해 functions-simulate 작업을 실행하세요:

npx hardhat functions-simulate --configpath REPLACE_CONFIG_PATH

예시:

$ npx hardhat functions-simulate --configpath tutorials/3-custom-response/config.js
secp256k1 unavailable, reverting to browser version

__Compiling Contracts__
Nothing to compile
Duplicate definition of Transfer (Transfer(address,address,uint256,bytes), Transfer(address,address,uint256))

Executing JavaScript request source code locally...

__Console log messages from sandboxed code__
HTTP GET Request to https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD
ETH price is: 1810.29 USD. 24h Volume is 113255.87 USD. Market: Coinbase

__Output from sandboxed source code__
Output represented as a hex string: 0x7b227072696365223a22313831302e3239222c22766f6c756d65223a223131333235352e3837222c226c6173744d61726b6574223a22436f696e62617365227d
Decoded as a string: {"price":"1810.29","volume":"113255.87","lastMarket":"Coinbase"}

__Simulated On-Chain Response__
Response returned to client contract represented as a hex string: 0x7b227072696365223a22313831302e3239222c22766f6c756d65223a223131333235352e3837222c226c6173744d61726b6574223a22436f696e62617365227d
Decoded as a string: {"price":"1810.29","volume":"113255.87","lastMarket":"Coinbase"}

Gas used by sendRequest: 376604
Gas used by client callback function: 98391

위의 예시 출력을 읽으면, ETH/USD 가격이 1810.29 USD이고, 거래량은 113255.87이며, 시장은 Coinbase임을 확인할 수 있습니다. 최종 결과가 JSON 객체이므로, 예시는 이를 문자열로 변환하고 콜백에서 bytes로 인코딩된 값 0x7b227072696365223a22313831302e3239222c22766f6c756d65223a223131333235352e3837222c226c6173744d61726b6574223a22436f696e62617365227d를 반환합니다. 자세한 설명은 소스 코드 설명을 참조하세요.

리퀘스트

Decentralized Oracle Network에 요청 보내기

자산 가격을 가져오기 위해 Decentralized Oracle Network에 요청을 보내세요. functions-request 작업을 실행하여 subid(구독 ID)와 contract 매개변수를 사용하세요. 이 작업은 배포된 FunctionsConsumer 계약에서 executeRequest 함수를 호출할 때 JavaScript 소스 코드, 인수 및 비밀을 전달합니다. 자세한 설명은 functionsConsumer 섹션을 참조하세요.

다음 명령어를 사용하여 functions-request 작업을 실행하세요:

npx hardhat functions-request --subid REPLACE_SUBSCRIPTION_ID --contract REPLACE_CONSUMER_CONTRACT_ADDRESS --network REPLACE_NETWORK --configpath REPLACE_CONFIG_PATH

예시:

$ npx hardhat functions-request --subid 443 --contract 0x4B4BA2Fd6b93aDF8d6b6002E10540E58394388Ea --network polygonMumbai --configpath tutorials/3-custom-response/config.js
secp256k1 unavailable, reverting to browser version
Estimating cost if the current gas price remains the same...

The transaction to initiate this request will charge the wallet (0x9d087fC03ae39b088326b67fA3C788236645b717):
0.00050942400509424 MATIC, which (using mainnet value) is $0.0005628062918760629

If the request's callback uses all 100,000 gas, this request will charge the subscription:
0.200148583233223418 LINK

Continue? Enter (y) Yes / (n) No
y
Simulating Functions request locally...

__Console log messages from sandboxed code__
HTTP GET Request to https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD
ETH price is: 1811.08 USD. 24h Volume is 113909.01 USD. Market: Bitfinex

__Output from sandboxed source code__
Output represented as a hex string: 0x7b227072696365223a22313831312e3038222c22766f6c756d65223a223131333930392e3031222c226c6173744d61726b6574223a2242697466696e6578227d
Decoded as a string: {"price":"1811.08","volume":"113909.01","lastMarket":"Bitfinex"}


⣾ Request 0x51d3aa3be08c71c49a37d586de07eb85e87a403e35a73521c5c914c430e2de2d has been initiated. Waiting for fulfillment from the Decentralized Oracle Network...

ℹ Transaction confirmed, see https://mumbai.polygonscan.com/tx/0xd0140c40e65fdf51ea1b4396258915438693b61cfacb6d28a368bdd4f94b0443 for more details.

✔ Request 0x51d3aa3be08c71c49a37d586de07eb85e87a403e35a73521c5c914c430e2de2d fulfilled! Data has been written on-chain.

Response returned to client contract represented as a hex string: 0x7b227072696365223a22313831312e3139222c227

66f6c756d65223a223131333931302e3933222c226c6173744d61726b6574223a2242697466696e6578227d
Decoded as a string: {"price":"1811.19","volume":"113910.93","lastMarket":"Bitfinex"}

Actual amount billed to subscription #443:
┌──────────────────────┬─────────────────────────────┐
│         Type         │           Amount            │
├──────────────────────┼─────────────────────────────┤
│  Transmission cost:  │  0.000071836060128269 LINK  │
│      Base fee:       │          0.2 LINK           │
│                      │                             │
│     Total cost:      │  0.200071836060128269 LINK  │
└──────────────────────┴─────────────────────────────┘

위 예시의 출력에서 다음 정보를 얻을 수 있습니다:

FunctionsConsumer 계약에서 executeRequest 함수가 성공적으로 호출되었습니다. 이 예시에서의 트랜잭션은 0xd0140c40e65fdf51ea1b4396258915438693b61cfacb6d28a368bdd4f94b0443입니다.
요청 ID는 0x51d3aa3be08c71c49a37d586de07eb85e87a403e35a73521c5c914c430e2de2d입니다.
DON이 요청을 성공적으로 수행했습니다. 총 비용은 0.200071836060128269 LINK입니다.
consumer 계약은 bytes 형식의 응답을 받았으며 값은 0x7b227072696365223a22313831312e3038222c22766f6c756d65223a223131333930392e3031222c226c6173744d61726b6574223a2242697466696e6578227d입니다. 이를 오프체인에서 문자열로 디코딩하면 다음 결과를 얻을 수 있습니다: {"price":"1811.19","volume":"113910.93","lastMarket":"Bitfinex"}.

함수 응답 읽기

언제든지 functions-read 작업을 실행하여 contract 매개변수를 사용하여 최신 수신된 응답을 읽을 수 있습니다.

다음 명령어를 사용하여 functions-read 작업을 실행하세요:

npx hardhat functions-read --contract REPLACE_CONSUMER_CONTRACT_ADDRESS --network REPLACE_NETWORK --configpath REPLACE_CONFIG_PATH

예시:

$ npx hardhat functions-read --contract 0x4B4BA2Fd6b93aDF8d6b6002E10540E58394388Ea --network polygonMumbai --configpath tutorials/3-custom-response/config.js
secp256k1 unavailable, reverting to browser version
Reading data from Functions client contract 0x4B4BA2Fd6b93aDF8d6b6002E10540E58394388Ea on network mumbai

On-chain response represented as a hex string: 0x7b227072696365223a22313831312e3139222c22766f6c756d65223a223131333931302e3933222c226c6173744d61726b6574223a2242697466696e6578227d
Decoded as a string: {"price":"1811.19","volume":"113910.93","lastMarket":"Bitfinex"}
Decoding 0x7b227072696365223a22313831312e3139222c22766f6c756d65223a223131333931302e3933222c226c6173744d61726b6574223a2242697466696e6578227d from bytes to string gives you {"price":"1811.19","volume":"113910.93","lastMarket":"Bitfinex"}. Off-chain, you can use JSON.parse() to convert the JSON string to a JSON object.

위 예시의 출력에서 다음 정보를 얻을 수 있습니다:

  • Functions 클라이언트 계약 0x4B4BA2Fd6b93aDF8d6b6002E10540E58394388Ea에서 데이터를 읽고 있습니다.
  • 체인 상의 응답은 0x7b227072696365223a22313831312e3139222c22766f6c756d65223a223131333931302e3933222c226c6173744d61726b6574223a2242697466696e6578227d로 표시됩니다.
  • 문자열로 디코딩한 결과는 {"price":"1811.19","volume":"113910.93","lastMarket":"Bitfinex"}입니다.
    0x7b227072696365223a22313831312e3139222c22766f6c756d65223a223131333931302e3933222c226c6173744d61726b6574223a2242697466696e6578227d를 바이트에서 문자열로 디코딩하면 {"price":"1811.19","volume":"113910.93","lastMarket":"Bitfinex"}를 얻을 수 있습니다. 오프체인에서는 JSON.parse()를 사용하여 JSON 문자열을 JSON 객체로 변환할 수 있습니다.

Explanation

FunctionsConsumer.sol

Chainlink Functions 소비자 컨트랙트를 작성하기 위해 컨트랙트는 FunctionsClient.sol을 import해야 합니다. API 참조: FunctionsClient.

이 컨트랙트는 NPM 패키지로 제공되지 않으므로 프로젝트 내에서 다운로드하고 import해야 합니다.

import {Functions, FunctionsClient} from "./dev/functions/FunctionsClient.sol";

Chainlink Functions 요청을 구축하는 데 필요한 모든 함수를 얻기 위해 Functions.sol 라이브러리를 사용합니다. API 참조: Functions.

using Functions for Functions.Request;

가장 최근의 요청 ID, 최근 수신된 응답 및 최근 수신된 오류(있는 경우)는 상태 변수로 정의됩니다. latestResponse 및 latestError는 동적으로 크기가 조정된 바이트 배열로 인코딩되므로 응답이나 오류를 읽으려면 여전히 디코딩해야 합니다.

bytes32 public latestRequestId;
bytes public latestResponse;
bytes public latestError;

스마트 컨트랙트가 콜백 중에 발생시킬 OCRResponse 이벤트를 정의합니다.

event OCRResponse(bytes32 indexed requestId, bytes result, bytes err);

컨트랙트를 배포할 때 네트워크의 오라클 주소를 전달합니다.

constructor(address oracle) FunctionsClient(oracle)

언제든지 updateOracleAddress 함수를 호출하여 오라클 주소를 변경할 수 있습니다.

남은 두 가지 함수는 다음과 같습니다:

  • executeRequest: 요청을 전송하는 함수입니다. JavaScript 소스 코드, 암호화된 비밀, 소스 코드에 전달할 인수 목록, 구독 ID 및 콜백 가스 제한을 매개변수로 받습니다. 다음과 같은 작업을 수행합니다:

    • Functions 라이브러리를 사용하여 요청을 초기화하고 전달된 암호화된 비밀이나 인수를 추가합니다. 요청 초기화, 비밀 추가, 인수 추가에 대한 API 참조를 확인할 수 있습니다.

      Functions.Request memory req;
      req.initializeRequest(Functions.Location.Inline, Functions.CodeLanguage.JavaScript, source);
      if (secrets.length > 0) {
        req.addRemoteSecrets(secrets);
      }
      if (args.length > 0) req.addArgs(args);
    • FunctionsClient의 sendRequest 함수를 호출하여 오라클에 요청을 전송합니다. 요청 ID를 latestRequestId에 저장합니다.

      bytes32 assignedReqID = sendRequest(req, subscriptionId, gasLimit);
      latestRequestId = assignedReqID;
  • fulfillRequest: 콜백 중에 호출되는 함수입니다. 이 함수는 FunctionsClient에서 virtual로 정의되어 있으

므로 스마트 컨트랙트는 콜백을 구현하기 위해 이 함수를 오버라이드해야 합니다. 콜백의 구현은 간단합니다. 컨트랙트는 OCRResponse 이벤트를 발생시키기 전에 latestResponse와 latestError에 최신 응답 및 오류를 저장합니다.

latestResponse = response;
latestError = err;
emit OCRResponse(requestId, response, err);

위와 같이 FunctionsConsumer.sol 컨트랙트를 작성할 수 있습니다.

config.js

각 설정에 대한 자세한 설명은 요청 구성 섹션을 참조하십시오. 이 예제에서는 다음 설정을 사용합니다:

  • codeLocation: Location.Inline: JavaScript 코드가 요청 내에 제공됩니다.
  • codeLanguage: CodeLanguage.JavaScript: 소스 코드가 JavaScript 언어로 작성되었습니다.
  • source: fs.readFileSync(path.resolve(__dirname, "source.js")).toString(): 소스 코드는 스크립트 객체여야 합니다. 이 예제에서는 fs.readFileSync를 사용하여 source.js를 읽고 toString()을 호출하여 문자열 객체로 내용을 가져옵니다.
  • args: ["ETH", "USD"]: 이러한 인수는 소스 코드에 전달됩니다. 이 예제는 ETH/USD 가격과 일일 거래량을 요청합니다.
  • expectedReturnType: ReturnType.string: DON(Direct Off-Chain API Consumer)에 의해 수신된 응답은 바이트로 인코딩됩니다. 가격, 일일 거래량 및 마켓이 JSON 문자열로 포함되어 있으므로 ReturnType.string을 정의하여 DON에 의해 수신된 응답을 해독하는 방법을 사용자에게 알려줍니다. 자세한 내용은 소스 코드 설명을 참조하십시오.

source.js

기대되는 API 응답을 확인할 수 있습니다. 다음 URL을 브라우저에 직접 붙여넣거나 터미널에서 curl 명령을 실행하세요.

curl -X 'GET' \
  'https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD' \
  -H 'accept: application/json'

응답은 다음 예시와 유사해야 합니다.

{
  "RAW": {
    "ETH": {
      "USD": {
        "TYPE": "5",
        "MARKET": "CCCAGG",
        "FROMSYMBOL": "ETH",
        "TOSYMBOL": "USD",
        "FLAGS": "2049",
        "PRICE": 2867.04,
        "LASTUPDATE": 1650896942,
        "MEDIAN": 2866.2,
        "LASTVOLUME": 0.16533939,
        "LASTVOLUMETO": 474.375243849,
        "LASTTRADEID": "1072154517",
        "VOLUMEDAY": 195241.78281014622,
        "VOLUMEDAYTO": 556240560.4621655,
        "VOLUME24HOUR": 236248.94641103,
        ...
      }
    }
  }
}

가격은 RAW, ETH, USD, PRICE에 위치하고, 거래량은 RAW, ETH, USD, VOLUME24HOUR에 있으며, 마켓은 RAW, ETH, USD, MARKET에 있습니다.

JavaScript 소스 코드 섹션을 읽어 호환되는 JavaScript 소스 코드를 작성하는 방법에 대한 자세한 설명을 확인할 수 있습니다. 이 JavaScript 소스

코드는 Functions.makeHttpRequest를 사용하여 HTTP 요청을 보냅니다. ETH/USD 가격을 요청하기 위해 소스 코드는 다음 URL을 호출합니다: https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD. Functions.makeHttpRequest의 문서를 읽으면 다음 매개변수를 제공해야 함을 알 수 있습니다.

코드는 자체적으로 설명되어 있으며 주석을 통해 모든 단계를 이해하는 데 도움이 됩니다. 주요 단계는 다음과 같습니다:

  • Functions.makeHttpRequest를 사용하여 cryptoCompareRequest HTTP 개체를 구성합니다.
  • HTTP 요청을 실행합니다.
  • 응답에서 자산 가격, 일일 거래량 및 마켓을 읽습니다.
  • JSON 개체를 구성합니다.
    const result = {
      price: price.toFixed(2),
      volume: volume.toFixed(2),
      lastMarket,
    }
  • JSON 개체를 JSON.stringify(result)를 사용하여 JSON 문자열로 변환합니다. 이 단계는 문자열을 바이트로 인코딩하기 전에 필수적입니다.
  • Functions.string 도우미 함수를 사용하여 결과를 버퍼로 반환합니다. 참고: Javascript Buffers에 대해 처음이거나 왜 중요한지 이해하려는 경우 이 문서를 읽으세요.

위와 같이 config.js와 source.js를 작성할 수 있습니다.

출처

https://docs.chain.link/chainlink-functions/tutorials/api-custom-response

profile
what doesn't kill you makes you stronger

0개의 댓글