smartcontractkit / smartcontractkit/chainlink
[NODE] Can’t create multi-variable response
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 8.2k
- Forks
- 2k
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 202
Description
[NODE] Can’t create multi-variable response
Description
On version 1.4.0-root, 1.4.1-root (maybe 1.3.0 too), job pipeline tasks successful completes, but submit_tx step (ethtx task) don't send transaction to Operator contract.
After downgrade to version 1.1.1, submit_tx sends transaction correctly!!!
Digging deep
Result of encode_tx step (ethabiencode task) and all previous steps in both Node version equals.
Temporary solve
Downgrade chainlink node to version 1.1.1
Steps to reproduce
- ... std steps to setup interaction with chainlink node jobs from your smart contract. Please, read docs for this.
- Call MultiWordConsumer.sol->fulfillMultipleParameters
- After transaction ends call by example, btc property. Just to make sure the value is 0. This incorrect behaviour, that should be equal to corresponding number from job run result. On version 1.1.1 value equals to correct value from job run
Environment
Docker version 20.10.16, build aa7e414
docker-compose version 1.29.2, build unknown
Compose spec
services:
eth-blockchain:
image: trufflesuite/ganache-cli:v6.12.2
command: --port 8545 --chainId 1337 --forkCacheSize 268435456 --debug --verbose --mnemonic "$HD_WALLET_MNEMONIC" --account_keys_path accounts.json --db /data/ganache
networks:
- blockchain
ports:
- 8545:8545
volumes:
- ./data/eth-blockchain/accounts/accounts.json:/app/accounts.json
- blockchain_data:/data/ganache
db-chainlink-node:
image: postgres:14.2-alpine
env_file:
- ./services/db-chainlink-node/.env
networks:
- blockchain
volumes:
- db_chainlink_data:/var/lib/postgresql/data
chainlink-node:
image: smartcontract/chainlink:1.4.0-root
command: local node -p /chainlink/.password -a /chainlink/.api
env_file:
- ./services/chainlink-node/.env.local
networks:
- blockchain
ports:
- 6688:6688
volumes:
- ./services/chainlink-node/.password:/chainlink/.password
- ./services/chainlink-node/.api:/chainlink/.api
- chainlink-node-data:/chainlink
networks:
blockchain:
driver: bridge
volumes:
chainlink-node-data:
driver: local
driver_opts:
type: none
device: "./data/chainlink-node"
o: bind
blockchain_data:
driver: local
driver_opts:
type: none
device: "./data/eth-blockchain/blockchain"
o: bind
db_chainlink_data:
driver: local
driver_opts:
type: none
device: "./data/db-chainlink-node"
o: bind
./services/chainlink-node/.env.local
ROOT=/chainlink
LOG_LEVEL=debug
ETH_CHAIN_ID=1337
CHAINLINK_TLS_PORT=0
SECURE_COOKIES=false
ALLOW_ORIGINS=*
ETH_URL=ws://eth-blockchain:8545
DATABASE_URL=postgresql://admin:admin@db-chainlink-node:5432/chainlink_node?sslmode=disable
CHAINLINK_DEV=true
CHAINLINK_PORT=6688
DEFAULT_HTTP_ALLOW_UNRESTRICTED_NETWORK_ACCESS=true
INSECURE_SKIP_VERIFY=true
./services/db-chainlink-node/.env
POSTGRES_USER=admin
POSTGRES_PASSWORD=admin
POSTGRES_DB=chainlink_node
./.env.local
HD_WALLET_MNEMONIC=stereo consider quality wild fat farm symptom bundle laundry side one lemon
./services/chainlink-node/.password:/chainlink/.password
stereo consider quality wild fat farm symptom bundle laundry side one lemon
./services/chainlink-node/.api:/chainlink/.api
user@example.com
password
multi_word_job.toml
This file equals to multi-variable job example from Chainlink official docs
type = "directrequest"
schemaVersion = 1
name = "multi-word (TOML)"
maxTaskDuration = "0s"
contractAddress = "YOUR_OPERATOR_CONTRACT_ADDRESS"
externalJobID = "438c9be2-7aff-4c58-bb7f-9632acaa418f"
minIncomingConfirmations = 0
observationSource = """
decode_log [type="ethabidecodelog"
abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)"
data="$(jobRun.logData)"
topics="$(jobRun.logTopics)"]
decode_cbor [type="cborparse" data="$(decode_log.data)"]
decode_log -> decode_cbor
decode_cbor -> btc
decode_cbor -> usd
decode_cbor -> eur
btc [type="http" method=GET url="$(decode_cbor.urlBTC)" allowunrestrictednetworkaccess="true"]
btc_parse [type="jsonparse" path="$(decode_cbor.pathBTC)" data="$(btc)"]
btc_multiply [type="multiply" input="$(btc_parse)", times="100000"]
btc -> btc_parse -> btc_multiply
usd [type="http" method=GET url="$(decode_cbor.urlUSD)" allowunrestrictednetworkaccess="true"]
usd_parse [type="jsonparse" path="$(decode_cbor.pathUSD)" data="$(usd)"]
usd_multiply [type="multiply" input="$(usd_parse)", times="100000"]
usd -> usd_parse -> usd_multiply
eur [type="http" method=GET url="$(decode_cbor.urlEUR)" allowunrestrictednetworkaccess="true"]
eur_parse [type="jsonparse" path="$(decode_cbor.pathEUR)" data="$(eur)"]
eurs_multiply [type="multiply" input="$(eur_parse)", times="100000"]
eur -> eur_parse -> eurs_multiply
btc_multiply -> encode_mwr
usd_multiply -> encode_mwr
eurs_multiply -> encode_mwr
// MWR API does NOT auto populate the requestID.
encode_mwr [type="ethabiencode"
abi="(bytes32 requestId, uint256 _btc, uint256 _usd, uint256 _eurs)"
data="{\\"requestId\\": $(decode_log.requestId), \\"_btc\\": $(btc_multiply), \\"_usd\\": $(usd_multiply), \\"_eurs\\": $(eurs_multiply)}"
]
encode_tx [type="ethabiencode"
abi="fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data)"
data="{\\"requestId\\": $(decode_log.requestId), \\"payment\\": $(decode_log.payment), \\"callbackAddress\\": $(decode_log.callbackAddr), \\"callbackFunctionId\\": $(decode_log.callbackFunctionId), \\"expiration\\": $(decode_log.cancelExpiration), \\"data\\": $(encode_mwr)}"
]
submit_tx [type="ethtx" to="YOUR_OPERATOR_CONTRACT_ADDRESS" data="$(encode_tx)"]
encode_mwr -> encode_tx -> submit_tx
"""
MultiWordConsumer.sol
This file so close (only constructor modified) to multi-variable response example contract from Chainlink official docs
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import '@chainlink/contracts/src/v0.8/ChainlinkClient.sol';
import '@chainlink/contracts/src/v0.8/ConfirmedOwner.sol';
/**
* Request testnet LINK and ETH here: https://faucets.chain.link/
* Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
*/
/**
* THIS IS AN EXAMPLE CONTRACT WHICH USES HARDCODED VALUES FOR CLARITY.
* PLEASE DO NOT USE THIS CODE IN PRODUCTION.
*/
contract MultiWordConsumer is ChainlinkClient, ConfirmedOwner {
using Chainlink for Chainlink.Request;
bytes32 private jobId;
uint256 private fee;
// multiple params returned in a single oracle response
uint256 public btc;
uint256 public usd;
uint256 public eur;
event RequestMultipleFulfilled(bytes32 indexed requestId, uint256 btc, uint256 usd, uint256 eur);
/**
* @notice Initialize the link token and target oracle
* @dev The oracle address must be an Operator contract for multiword response
*
*
* Kovan Testnet details:
* Link Token: 0xa36085F69e2889c224210F603D836748e7dC0088
* Oracle: 0x74EcC8Bdeb76F2C6760eD2dc8A46ca5e581fA656 (Chainlink DevRel)
* jobId: 53f9755920cd451a8fe46f5087468395
*
*/
constructor(address _linkToken, address _operator) ConfirmedOwner(msg.sender) {
setChainlinkToken(_linkToken);
setChainlinkOracle(_operator);
jobId = '438c9be27aff4c58bb7f9632acaa418f';
fee = (1 * LINK_DIVISIBILITY) / 10; // 0,1 * 10**18 (Varies by network and job)
}
/**
* @notice Request mutiple parameters from the oracle in a single transaction
*/
function requestMultipleParameters() public {
Chainlink.Request memory req = buildChainlinkRequest(
jobId,
address(this),
this.fulfillMultipleParameters.selector
);
req.add('urlBTC', 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=BTC');
req.add('pathBTC', 'BTC');
req.add('urlUSD', 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD');
req.add('pathUSD', 'USD');
req.add('urlEUR', 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=EUR');
req.add('pathEUR', 'EUR');
sendChainlinkRequest(req, fee); // MWR API.
}
/**
* @notice Fulfillment function for multiple parameters in a single request
* @dev This is called by the oracle. recordChainlinkFulfillment must be used.
*/
function fulfillMultipleParameters(
bytes32 requestId,
uint256 btcResponse,
uint256 usdResponse,
uint256 eurResponse
) public recordChainlinkFulfillment(requestId) {
emit RequestMultipleFulfilled(requestId, btcResponse, usdResponse, eurResponse);
btc = btcResponse;
usd = usdResponse;
eur = eurResponse;
}
/**
* Allow withdraw of Link tokens from the contract
*/
function withdrawLink() public onlyOwner {
LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
require(link.transfer(msg.sender, link.balanceOf(address(this))), 'Unable to transfer');
}
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with multi_word_job.toml, MultiWordConsumer.sol, and the submit_tx ethtx step, then reproduce the job with the supplied Docker Compose configuration on Chainlink node 1.4.0-root and compare it with 1.1.1. Done means the transaction is sent and the btc, usd, and eur values are populated from the job result.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, docker-compose, postgresql, solidity
- Domain
- backend, blockchain
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 32/100