How to read a JSON file using a chainlink oracle - json

I've altered the chainlink APIconsumer example to read a JSON file which contains data that I wish to bring into and store in the smart contract
pragma solidity ^0.6.0;
import "https://raw.githubusercontent.com/smartcontractkit/chainlink/develop/evm-contracts/src/v0.6/ChainlinkClient.sol";
contract APIConsumer is ChainlinkClient {
string public Name;
address private oracle;
bytes32 private jobId;
uint256 private fee;
/**
* Network: Kovan
* Oracle: Chainlink - 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e
* Job ID: Chainlink - 50fc4215f89443d185b061e5d7af9490
* Fee: 0.1 LINK
*/
constructor() public {
setPublicChainlinkToken();
oracle = 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e;
jobId = "50fc4215f89443d185b061e5d7af9490";
fee = 0.1 * 10 ** 18; // 0.1 LINK
}
/**
* Create a Chainlink request to retrieve API response, find the target price
* data, then multiply by 100 (to remove decimal places from price).
*/
function requestAthleteData() public returns (bytes32 requestId)
{
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// Set the URL to perform the GET request on
request.add("get", "https://1e68b62e-578e-4390-bf43-6b70a92a23b6.mock.pstmn.io/get");
// Set the path to find the desired data in the API response, where the response format is:
// {"USD":243.33}
request.add("path", "Name");
// Multiply the result by 100 to remove decimals
// request.addInt("times", 100);
// Sends the request
return sendChainlinkRequestTo(oracle, request, fee);
}
/**
* Receive the response in the form of uint256
*/
function fulfill(bytes32 _requestId, string memory _name) public recordChainlinkFulfillment(_requestId)
{
Name = _name;
}
}
This is the data its trying to read:
https://1e68b62e-578e-4390-bf43-6b70a92a23b6.mock.pstmn.io/get
[ { "Name": "Tom", "Birthday": "2021-07-01", "Nationality": "SA", "Address": "123 st st" } ]
It has no problems deploying but then when I call the function 'requesttAthletedata' it processes it but doesn't return anything. Am I missing a step somewhere? Or is it a problem with the code?

First, change the _name argument in fulfill() to bytes32.
Second, change your request path to this:
request.add("path", "0.Name");
Chainlink cannot currently write strings to a smart contract, only bytes32 which can then be converted into a string. Also, your JSON object was within an array (at the first index) which is why we need to specify "0.Name" as the JSON path.
Third, if you want to convert the bytes32 to a string within the smart contract, you will need to do it within the fulfill() method.
Your final code should look like this:
pragma solidity ^0.6.0;
import "#chainlink/contracts/src/v0.6/ChainlinkClient.sol";
contract test is ChainlinkClient {
string public Name;
address private oracle;
bytes32 private jobId;
uint256 private fee;
/**
* Network: Kovan
* Oracle: Chainlink - 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e
* Job ID: Chainlink - 50fc4215f89443d185b061e5d7af9490
* Fee: 0.1 LINK
*/
constructor() public {
setPublicChainlinkToken();
oracle = 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e;
jobId = "50fc4215f89443d185b061e5d7af9490";
fee = 0.1 * 10 ** 18; // 0.1 LINK
}
/**
* Create a Chainlink request to retrieve API response, find the target price
* data, then multiply by 100 (to remove decimal places from price).
*/
function requestAthleteData() public returns (bytes32 requestId)
{
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// Set the URL to perform the GET request on
request.add("get", "https://1e68b62e-578e-4390-bf43-6b70a92a23b6.mock.pstmn.io/get");
// Set the path to find the desired data in the API response, where the response format is:
// {"USD":243.33}
request.add("path", "0.Name");
// Multiply the result by 100 to remove decimals
// request.addInt("times", 100);
// Sends the request
return sendChainlinkRequestTo(oracle, request, fee);
}
/**
* Receive the response in the form of uint256
*/
function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) {
uint8 i = 0;
while(i < 32 && _bytes32[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
bytesArray[i] = _bytes32[i];
}
return string(bytesArray);
}
function fulfill(bytes32 _requestId, bytes32 _name) public recordChainlinkFulfillment(_requestId)
{
string memory stringName = bytes32ToString(_name);
Name = stringName;
}
}

Related

How to define TYPEHASH for EIP712 typed data signing with nested struct in Solidity?

I am wondering what the correct way is to define the TYPEHASH for a nested struct data structure for the EIP-712. I am trying to do this, as I want to retrieve the signer of a request struct using ECDSA and the EIP-712 standard for hashing structs.
This is the contract:
import "#openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "#openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract SignatureChecker is EIP712 {
using ECDSA for bytes32;
struct Fee {
address recipient;
uint256 value;
}
struct Request {
address to;
address from;
Fee[] fees;
}
bytes32 public TYPEHASH = keccak256("Request(address to,address from, Fee[] fees)");
constructor() EIP712("SignatureChecker", "1") {}
function verify(
Request calldata request,
bytes calldata signature,
address supposedSigner
) external view returns (bool) {
return recoverAddress(request, signature) == supposedSigner;
}
function recoverAddress(
Request calldata request,
bytes calldata signature
) public view returns (address) {
return _hashTypedDataV4(keccak256(encodeRequest(request))).recover(signature);
}
function encodeRequest(Request calldata request) public view returns (bytes memory) {
return abi.encode(TYPEHASH, request.to, request.from, request.fees);
}
}
I just want to make sure that I am encoding the request correctly in the encodeRequest function. Unfortunately I could not find anything on how to create a typehash of a nested struct. Is the way I am creating the typehash correct?
When I tried out the verify function without the fees property and the different TYPEHASH without the fee, it worked completely fine. However when I try to retrieve the address of a signature of the request struct with the fees array, it returns a wrong address.
I have also seen an example where someone tried to do this:
bytes32 public constant TYPEHASH = keccak256("Request(address to,address from, Fee[] fees)Fee(address recipient, uint256 value)");
Unfortunately it also produces a wrong address.
After doing quite a lot of research (including reading the entire EIP-712), I could craft a solution, which works:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "#openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "#openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract SignatureChecker is EIP712 {
using ECDSA for bytes32;
struct Fee {
address recipient;
uint256 value;
}
struct Request {
address to;
address from;
Fee[] fees;
}
bytes32 public constant FEE_TYPEHASH = keccak256("Fee(address recipient,uint256 value)");
bytes32 public constant REQUEST_TYPEHASH =
keccak256(
"Request(address to,address from,Fee[] fees)Fee(address recipient,uint256 value)"
);
constructor() EIP712("SignatureChecker", "1") {}
function verify(
Request calldata request,
bytes calldata signature,
address signer
) external view returns (bool) {
return recoverAddressOfRequest(request, signature) == signer;
}
function recoverAddressOfRequest(
Request calldata request,
bytes calldata signature
) public view returns (address) {
return _hashTypedDataV4(keccak256(encodeRequest(request))).recover(signature);
}
function recoverAddressOfFee(
Fee calldata fee,
bytes calldata signature
) public view returns (address) {
return _hashTypedDataV4(keccak256(encodeFee(fee))).recover(signature);
}
function encodeFee(Fee calldata fee) public pure returns (bytes memory) {
return abi.encode(FEE_TYPEHASH, fee.recipient, fee.value);
}
function encodeRequest(Request calldata request) public pure returns (bytes memory) {
bytes32[] memory encodedFees = new bytes32[](request.fees.length);
for (uint256 i = 0; i < request.fees.length; i++) {
encodedFees[i] = keccak256(encodeFee(request.fees[i]));
}
return
abi.encode(
REQUEST_TYPEHASH,
request.to,
request.from,
keccak256(abi.encodePacked(encodedFees))
);
}
}
The main problem was, that in order for this to work, you have to encode the every Fee element inside of the Request struct individually, and hash the resulting array to append it to the encoded request.

How do I use a Chainlink api call to retrieve a string value from json returne by api

I am trying to use a chainlink request to make an api call, then update the volume variable with the result of the api call.
The api call should retrieve a string. After deploying and funding the smart contract it seems I am able to make the api call successfully (although I'm not sure about this). The problem is the volume variable isn't updating.
I adapted the code from a chainlink tutorial. The original tutorial code works, to my mind the changes I made should also work. Can anyone help with this?
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/ChainlinkClient.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 APIConsumer is ChainlinkClient {
using Chainlink for Chainlink.Request;
string public volume;
address private oracle;
bytes32 private jobId;
uint256 private fee;
/**
* Network: Kovan
* Oracle: 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8 (Chainlink Devrel
* Node)
* Job ID: d5270d1c311941d0b08bead21fea7747
* Fee: 0.1 LINK
*/
constructor() {
setPublicChainlinkToken();
oracle = 0xF405B99ACa8578B9eb989ee2b69D518aaDb90c1F;
jobId = "c51694e71fa94217b0f4a71b2a6b565a";
fee = 0.1 * 10 ** 18; // (Varies by network and job)
}
/**
* Create a Chainlink request to retrieve API response, find the target
* data, then multiply by 1000000000000000000 (to remove decimal places from data).
*/
function requestVolumeData() public returns (bytes32 requestId)
{
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// Set the URL to perform the GET request on
request.add("get", "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD");
// Set the path to find the desired data in the API response, where the response format is:
// {"RAW":
// {"ETH":
// {"USD":
// {
// "VOLUME24HOUR": xxx.xxx,
// }
// }
// }
// }
request.add("path", "RAW.ETH.USD.MARKET");
// // Multiply the result by 1000000000000000000 to remove decimals
// int timesAmount = 10**18;
// request.addInt("times", timesAmount);
// Sends the request
return sendChainlinkRequestTo(oracle, request, fee);
}
/**
* Receive the response in the form of uint256
*/
function fulfill(bytes32 _requestId, bytes32 _volume) public recordChainlinkFulfillment(_requestId)
{
volume = bytes32ToString(_volume);
}
function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) {
uint8 i = 0;
while(i < 32 && _bytes32[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
bytesArray[i] = _bytes32[i];
}
return string(bytesArray);
}
// function withdrawLink() external {} - Implement a withdraw function to avoid locking your LINK in the contract
}
To return a string, you actually have to return a bytes32 and cast it to a string on-chain.
You can use something like this, to turn a bytes32 to string:
function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) {
uint8 i = 0;
while(i < 32 && _bytes32[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
bytesArray[i] = _bytes32[i];
}
return string(bytesArray);
}
Here is one working example with AviationStack. Replace the <access_key> with your access_key, in the API URL.
//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 THAT USES HARDCODED VALUES FOR CLARITY.
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
* DO NOT USE THIS CODE IN PRODUCTION.
*/
contract AviationGenericLargeResponse is ChainlinkClient, ConfirmedOwner {
using Chainlink for Chainlink.Request;
// variable bytes(arbitrary-length raw byte data) returned in a single oracle response
bytes public data;
string public flightStatus;
bytes32 private jobId;
uint256 private fee;
event RequestFulfilled(bytes32 indexed requestId, bytes indexed data);
/**
* #notice Initialize the link token and target oracle
* #dev The oracle address must be an Operator contract for multiword response
*
*
* Goerli Testnet details:
* Link Token: 0x326C977E6efc84E512bB9C30f76E30c160eD06FB
* Oracle: 0xCC79157eb46F5624204f47AB42b3906cAA40eaB7 (Chainlink DevRel)
* jobId: 7da2702f37fd48e5b1b9a5715e3509b6
*
*/
constructor() ConfirmedOwner(msg.sender) {
setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
setChainlinkOracle(0xCC79157eb46F5624204f47AB42b3906cAA40eaB7);
jobId = '7da2702f37fd48e5b1b9a5715e3509b6';
fee = (1 * LINK_DIVISIBILITY) / 10; // 0,1 * 10**18 (Varies by network and job)
}
/**
* #notice Request variable bytes from the oracle
*/
function requestBytes() public {
Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfillBytes.selector);
req.add(
'get',
'http://api.aviationstack.com/v1/flights?access_key=a6656fabf7fbe4fde6e4061f5be7042a&flight_number=611&airline_name=Qantas&flight_status=landed&limit=1'
);
req.add('path', 'data,0,flight_status');
sendChainlinkRequest(req, fee);
}
/**
* #notice Fulfillment function for variable bytes
* #dev This is called by the oracle. recordChainlinkFulfillment must be used.
*/
function fulfillBytes(bytes32 requestId, bytes memory bytesData) public recordChainlinkFulfillment(requestId) {
emit RequestFulfilled(requestId, bytesData);
data = bytesData;
flightStatus = string(data);
}
/**
* 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');
}
}

Chainlink v0.8 Smart Contract Deployment Issue

I am having issues verifying my smart contract on Rinkeby Testnet using #chainlink. My contract is deploying to Rinkeby fine but runs into issues when I have publish_source on. The contract verifies fine when using v0.6 and Solidity 0.6.6, I seem to only have issues with v0.8. Anyone know why this might be happening?
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#chainlink/contracts/src/v0.8/ChainlinkClient.sol";
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
/**
* #title MyContract is an example contract which requests data from
* the Chainlink network
* #dev This contract is designed to work on multiple networks, including
* local test networks
*/
contract MyContract is ChainlinkClient, ERC721 {
using Chainlink for Chainlink.Request;
address constant RINKEBY_ORACLE =
0x3A56aE4a2831C3d3514b5D7Af5578E45eBDb7a40;
bytes32 constant RINKEBY_JOB_ID =
bytes32("187bb80e5ee74a139734cac7475f3c6e");
uint256 constant FEE = 0.1 * 10**18;
uint256 public tokenCounter;
bytes32 public data;
string public image_url;
struct Clip {
string name;
string url;
}
Clip[] public clips;
mapping(bytes32 => string) public requestIdToClipName;
mapping(bytes32 => address) public requestIdToSender;
mapping(bytes32 => string) public requestIdToTokenURI;
event requestedCollectible(bytes32 indexed requestId);
mapping(bytes32 => uint256) public requestToTokenId;
mapping(uint256 => string) public tokenIdToName;
mapping(uint256 => string) public tokenIdToImgUrl;
constructor(address _link) public ERC721("Tests", "TST") {
if (_link == address(0)) {
setPublicChainlinkToken();
} else {
setChainlinkToken(_link);
}
setChainlinkOracle(0x3A56aE4a2831C3d3514b5D7Af5578E45eBDb7a40);
tokenCounter = 0;
}
function getChainlinkToken() public view returns (address) {
return chainlinkTokenAddress();
}
function createRequestTo(
string memory _url,
string memory _path,
string memory clipName
) public returns (bytes32 requestId) {
Chainlink.Request memory req = buildChainlinkRequest(
RINKEBY_JOB_ID,
address(this),
this.fulfill.selector
);
req.add("get", _url);
req.add("path", _path);
requestId = sendChainlinkRequestTo(RINKEBY_ORACLE, req, FEE);
requestIdToClipName[requestId] = clipName;
requestIdToSender[requestId] = msg.sender;
emit requestedCollectible(requestId);
return requestId;
}
function fulfill(bytes32 _requestId, bytes32 _data)
public
recordChainlinkFulfillment(_requestId)
{
address nftOwner = requestIdToSender[_requestId];
string memory name = requestIdToClipName[_requestId];
uint256 newItemId = clips.length;
data = _data;
image_url = bytes32ToString(data);
clips.push(Clip(name, image_url));
_safeMint(nftOwner, newItemId);
tokenIdToName[newItemId] = name;
tokenIdToImgUrl[newItemId] = image_url;
}
function getNumberOfClips() public view returns (uint256) {
return clips.length;
}
/**
* #notice Allows the owner to withdraw any LINK balance on the contract
*/
function withdrawLink() public {
LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
require(
link.transfer(msg.sender, link.balanceOf(address(this))),
"Unable to transfer"
);
}
/**
* #notice Call this method if no response is received within 5 minutes
* #param _requestId The ID that was generated for the request to cancel
* #param _payment The payment specified for the request to cancel
* #param _callbackFunctionId The bytes4 callback function ID specified for
* the request to cancel
* #param _expiration The expiration generated for the request to cancel
*/
function cancelRequest(
bytes32 _requestId,
uint256 _payment,
bytes4 _callbackFunctionId,
uint256 _expiration
) public {
cancelChainlinkRequest(
_requestId,
_payment,
_callbackFunctionId,
_expiration
);
}
}
Here is the script I am using to deploy:
from brownie import MyContract, accounts, network, config
from scripts.helpful_scripts import fund_contract
def main():
dev = accounts.add(config['wallets']['from_key'])
print(network.show_active())
publish_source = True
my_contract = MyContract.deploy(
config['networks'][network.show_active()]['link_token'],
{"from": dev},
publish_source=publish_source
)
fund_contract(my_contract)
return my_contract
This is the error:
Transaction sent: 0x01ef409a5b0c2f6de60194f14f1d2e8ffac9a6ce6397aaa38392d949e2656909
Gas price: 1.000000008 gwei Gas limit: 2793499 Nonce: 359
MyContract.constructor confirmed Block: 9332417 Gas used: 2539545 (90.91%)
MyContract deployed at: 0x42fBa06808C3cB11e5D7AcC6BB02B1A612e040d1
File "brownie/_cli/run.py", line 49, in main
return_value, frame = run(
File "brownie/project/scripts.py", line 103, in run
return_value = f_locals[method_name](*args, **kwargs)
File "./scripts/deploy_api.py", line 11, in main
my_contract = MyContract.deploy(
File "brownie/network/contract.py", line 600, in __call__
return tx["from"].deploy(
File "brownie/network/account.py", line 555, in deploy
contract.publish_source(deployed_contract, silent=silent)
File "brownie/network/contract.py", line 410, in publish_source
contract_info = self.get_verification_info()
File "brownie/network/contract.py", line 292, in get_verification_info
if symbol_alias["local"] is not None:
KeyError: 'local'
Two points going on here:
Brownie Etherscan verification doesn't work well with Solidity v0.8. Work is being done to fix this issue.
Looks like you are deploying to a local network instead of Rinkeby. Either remove publish_source when deploying locally or add --network rinkeby to the end of your deploy script to deploy to Rinkeby.

How to encode tuple as input parameter to function using web3j

I am attempting to call a solidity function that looks something like the following:
function fillOrder(
Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature
)
Using web3j I would create the function similar to below, however I'm not quite sure how to represent the order which is represented as a struct in Solidity.
List<Type> inputParams = Arrays.asList(???, new
Uint256(takerAssetFillAmount), new Bytes32(signture));
new Function("fillOrder", inputParams, Collections.emptyList());
Any pointers on how I should represent the struct?
Thanks.
You can wrap parameters with square brackets.
For example, let's say I have a contract:
contract Test {
struct Foo {
uint a;
string b;
address c;
}
function bar (Foo memory foo) public {
c = foo.c;
}
}
I can call bar function with web3.js like this:
contract.methods.foo([123, "123", "0xABC...."]).send({ from: '0x...' })
here is the contract address https://goerli.etherscan.io/address/0xd5999bf0ce31a1d9d6a6de2bf03feaff1913cee5#writeContract
in the write function , createSwapOrder is asking nested Tuple . here is the solidity code to show the structure of tuple :
struct Side {
address user;
bytes signedRequiredOutput;
ERC20Component[] erc20s;
ERC721Component[] erc721s;
ERC1155Component[] erc1155s;
}
struct ERC20Component {
uint256 amount;
address underlying;
// A signed approval transaction giving `amount` transfer rights
// of token `underlying` to address(this).
// bytes signedApproval;
}
struct ERC721Component {
uint256 tokenId;
address collection;
// A signed approval transaction giving `tokenId` tranfer rights
// of token `collection` to address(this).
// bytes signedApproval;
}
struct ERC1155Component {
uint256 tokenId;
uint256 amount;
address collection;
// A signed approval transaction giving `tokenId` tranfer rights
// of token `collection` to address(this).
// bytes signedApproval;
}
struct Order {
Side side0;
Side side1;
uint256 expiry;
bytes32 hashlock;
bytes32 preimage;
bool completed;
}
event OrderCreated(address indexed user, bytes32 orderId);
uint256 public totalOrders;
mapping(bytes32 => Order) public orders;
function createSwapOrder(
Side calldata side0,
bytes32 hashlock,
uint256 timelock
) public {
...
}
and in first args side0 is asking a nested tuple and this tuple formet should be like this
["0x5B38Da6a701c568545dCfcB03FcB875f56beddC4","0x00",[["32","0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"]],[["32","0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"]],[["32","32","0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"]]],
i hope you can understand the structure how its provided !! and sure it working
Web3j offers such classes as StaticStruct and DynamicStruct where you define your struct object via primitives. Here is the sample from my project:
class Value: DynamicStruct {
private lateinit var offer: String
private lateinit var availableSince: BigInteger
private lateinit var availabilityEnd: BigInteger
private var isConsumed: Boolean = false
private lateinit var lockedUntil: BigInteger
constructor(
offer: String,
availableSince: BigInteger,
availabilityEnd: BigInteger,
isConsumed: Boolean,
lockedUntil: BigInteger
) : super(
Utf8String(offer), Uint256(availableSince),
Uint256(availabilityEnd), Bool(isConsumed),
Uint256(lockedUntil)
) {
this.offer = offer
this.availableSince = availableSince
this.availabilityEnd = availabilityEnd
this.isConsumed = isConsumed
this.lockedUntil = lockedUntil
}
constructor(
offer: Utf8String,
availableSince: Uint256,
availabilityEnd: Uint256,
isConsumed: Bool,
lockedUntil: Uint256
) : super(offer, availableSince, availabilityEnd, isConsumed, lockedUntil) {
this.offer = offer.value
this.availableSince = availableSince.value
this.availabilityEnd = availabilityEnd.value
this.isConsumed = isConsumed.value
this.lockedUntil = lockedUntil.value
}
}
Ideally you just need to pass this struct instance to you contract method as a parameter where contract is autogenerated over $web3j solidity generate -b /path/to/<smart-contract>.bin -a /path/to/<smart-contract>.abi -o /path/to/src/main/java -p com.your.organisation.name command.
However personally I faced with issue to make this console command working and have to implement required logic by my own. This is in the progress now.

What is the best approach in Solidity for pagination (straightforward and based on some filter(s)) capabilities?

Let's suppose that we have following structures in Solidity contract :
struct EntityA {
string lessThen32ByteString1;
string moreThen32ByteString1;
string lessThen32ByteString2;
string moreThen32ByteString3;
bool flag;
uint var1;
uint var2;
uint var3;
uint var4;
ProposalStatus proposalStatus;}
// 100K entities EntityA[] public items;
We have now following implementation (with helper functions for string to byte32 conversion, splitting string to a few byte32 parts and so on) :
function getChunkOfPart1EntityADetails(uint filterAsUint, uint offset, uint limit) public constant
returns (bytes32[100] lessThen32ByteString1Arr, bytes32[100] moreThen32ByteString1PrefixArr, bytes32[100] moreThen32ByteString1SuffixArr) {
}
function getChunkOfPart2EntityADetails(uint filterAsUint, uint offset, uint limit) public constant
returns (bytes32[100] lessThen32ByteString2Arr, bytes32[100] moreThen32ByteString2PrefixArr, bytes32[100] moreThen32ByteString2SuffixArr) {
}
function getChunkOfPart3EntityADetails(uint filterAsUint, uint offset, uint limit) public constant
returns (bool[100] flagArr, uint[100] var1Arr, uint[100] var2Arr, uint[100] var3Arr, uint[100] var4Arr, ProposalStatus[100] proposalStatusArr,) {
}
Current implementation does not look good from design perspective, furthermore each additional filter support requires contract changes due to absence any query language support.
Use case example : retrieve 10 entities based on specified proposalStatus and offset 50 (6th page, page size is 10 entities (maybe 20, 50, 100))
I know I'm late but here you go. This will return an array of uints. You can then use this array on the client to resolve the id's in the getter for the array that stores your data.
struct Zombie {
//data
};
Zombie[] public zombies;
function paginateAllZombies(uint _resultsPerPage, uint _page) external view returns (uint[]) {
Zombie[] memory result = new Zombie[](_resultsPerPage);
for(uint i = _resultsPerPage * _page - _resultsPerPage; i < _resultsPerPage * _page; i++ ){
result[i] = i;
} //CONVERT TO SAFEMATH
return result;
}
So once you get this returned you can .map() the result in your client app and call zombies(uint _index).
If you need more details on this approach please check out cryptozombies.io
Not sure if this is the best approach though.
Hopefully in the future you can just do:
function paginateAllZombies(uint _resultsPerPage, uint _page) external view returns (Zombie[]) {
uint[] memory result = new uint[](_resultsPerPage);
for(uint i = _resultsPerPage * _page - _resultsPerPage; i < _resultsPerPage * _page; i++ ){
result[i] = zombies[i];
} //CONVERT TO SAFEMATH
return result;
}