I have 2 Smart Contracts, the StockAPI which is a ChainlinkClient and the RepToken Smart Contract. The StockAPI is the Chainlink API Consumer and is called by RepToken via the evaluatePredictions
function. Now I want to be able to return the API response back to the RepToken Smart Contract via the fulfill function.
I already looked at this example and did exactly what was proposed:
How to return a Chainlink API Call to another contract?
According to that, I created the following Smart Contracts:
contract StockAPI is ChainlinkClient {
constructor() public {
owner = payable(msg.sender);
setPublicChainlinkToken();
oracle = 0x3A56aE4a2831C3d3514b5D7Af5578E45eBDb7a40;
jobId = "187bb80e5ee74a139734cac7475f3c6e";
fee = 0.01 * 10**18; // 0.01 LINK
}
address payable public owner;
address private oracle;
uint256 private fee;
bytes32 private jobId;
function requestStockPrice(
address _cbContract,
bytes4 _cbFunction,
string calldata _symbol,
string calldata _date
) external returns (bytes32 requestId) {
Chainlink.Request memory request =
buildChainlinkRequest(jobId, _cbContract, _cbFunction);
request.add(
"get",
string(
abi.encodePacked(
"https://api.twelvedata.com/time_series?symbol=",
_symbol,
"&exchange=XETR&start_date=",
_date,
"&end_date=",
_date,
"&interval=1min&apikey=key"
)
)
);
request.add("path", "values.0.close");
return sendChainlinkRequestTo(oracle, request, fee);
}
}
contract RepToken is ChainlinkClient {
using SafeMath for uint256;
PredictionsDB private predictionsDB;
StockAPI private stockAPI;
constructor(PredictionsDB _predictionsDB, StockAPI _stockAPI) public {
owner = payable(msg.sender);
predictionsDB = _predictionsDB;
stockAPI = _stockAPI;
}
address payable public owner;
string public constant symbol = "REP";
string public constant name = "REP Token";
uint256 public totalSupply;
uint256 public volume;
uint8 public decimals = 18;
bytes32[] public ids;
struct Prediction {
address predictor;
string symbol;
string date;
uint256 unixDate;
uint256 price;
bool checked;
}
mapping(address => uint256) public balanceOf;
mapping(bytes32 => Prediction) public requestMapping;
event RepTokensMinted(address indexed to, uint256 totalSupply);
event RepTokensBurned(address indexed from, uint256 totalSupply);
function evaluatePredictions(address _predictor) external {
(
string[] memory symbols,
string[] memory dates,
uint256[] memory unixDates,
uint256[] memory prices,
bool[] memory checks
) = predictionsDB.getPredictions(_predictor);
require(
_predictor == msg.sender && prices.length > 0,
"Not the predictor or no predictions!"
);
for (uint256 i = 0; i < prices.length; i = i.add(1)) {
if (unixDates[i] > block.timestamp || checks[i] == true) {
continue;
} else {
bytes32 requestId =
stockAPI.requestStockPrice(
address(this),
this.fulfillEvaluation.selector,
symbols[i],
dates[i]
);
checks[i] = true;
requestMapping[requestId] = Prediction(
_predictor,
symbols[i],
dates[i],
unixDates[i],
prices[i],
checks[i]
);
ids.push(requestId);
}
}
}
function parseInt(string memory _a, uint256 _b)
private
pure
returns (uint256)
{
...
}
function mint(address _predictor) private {
...
}
function burn(address _predictor) private {
...
}
function fulfillEvaluation(bytes32 _requestId, bytes32 _close)
public
recordChainlinkFulfillment(_requestId)
{
uint256 i;
while (i < 32 && _close[i] != 0) {
i = i.add(1);
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && _close[i] != 0; i = i.add(1)) {
bytesArray[i] = _close[i];
}
uint256 close = parseInt(string(bytesArray), 5);
if (requestMapping[_requestId].price <= close) {
mint(requestMapping[_requestId].predictor);
} else {
burn(requestMapping[_requestId].predictor);
}
delete requestMapping[_requestId];
}
}
The function stockPriceAPI.requestStockPrice
calls the method in the StockAPI contract and passes the address of the RepToken contract, the selector of the fulfillEvaluation
function as well as a stock symbol and a date as a string for the API url.
The request is then executed in the StockAPI contract and now I would expect to receive the value in the fulfillEvaluation
callback function in RepToken contract, but it seems that it doesn't receive any value. The API call itself definitely works, because I can see calls in the API dashboard, but somehow the data doesn't come back to the RepToken contract.
I would be glad if someone can help.