Ok! I solved it.
First i created my own Contract, which is extend Contract from org.web3j.tx. Overrided two methods (i need to receive multiple values from contract): executeCallMultipleValueReturnAsync, executeCallMultipleValueReturn but with identical content as in Contract and created own executeCall method, which is identical to private executeCall from web3j Contract, but instead of pass DefaultBlockParameterName.LATEST to web3j.ethCall i am passing new DefaultBlockParameterNumber(blockNumber) with desired blockNumber as BigInteger. So here is:
public class MyEthereumContract extends Contract {
private String contractAddress;
private BigInteger blockNumber;
protected MyEthereumContract(String contractBinary, String contractAddress, Web3j web3j,
TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
super(contractBinary, contractAddress, web3j, transactionManager, gasPrice, gasLimit);
}
protected MyEthereumContract(String contractBinary, String contractAddress, Web3j web3j, Credentials credentials,
BigInteger gasPrice, BigInteger gasLimit) {
super(contractBinary, contractAddress, web3j, credentials, gasPrice, gasLimit);
}
protected MyEthereumContract(String contractBinary, String contractAddress, Web3j web3j, Credentials credentials,
BigInteger gasPrice, BigInteger gasLimit,
BigInteger blockNumber) {
super(contractBinary, contractAddress, web3j, credentials, gasPrice, gasLimit);
this.contractAddress = contractAddress;
this.blockNumber = blockNumber;
}
@Override
protected CompletableFuture<List<Type>> executeCallMultipleValueReturnAsync(Function function) {
return Async.run(() -> executeCallMultipleValueReturn(function));
}
@Override
protected List<Type> executeCallMultipleValueReturn(
Function function) throws InterruptedException, ExecutionException {
return executeCall(function);
}
private List<Type> executeCall(
Function function) throws InterruptedException, ExecutionException {
String encodedFunction = FunctionEncoder.encode(function);
org.web3j.protocol.core.methods.response.EthCall ethCall = web3j.ethCall(
Transaction.createEthCallTransaction(
transactionManager.getFromAddress(), contractAddress, encodedFunction),
new DefaultBlockParameterNumber(blockNumber))
.sendAsync().get();
String value = ethCall.getValue();
return FunctionReturnDecoder.decode(value, function.getOutputParameters());
}
}
Than i added new constructor to class, generated from contract, which i am calling from load method
You have to remove the first four bytes first. The first four bytes of a contract call is the function selector, which specifies which function you want to call, in this case balanceOf(address)
. I wrote a more detailed explanation about transaction data, which you can find here.
Assuming you're using Solidity 0.6.0 or newer, you can use data[4:]
to slice off the first four bytes of the input, or additionally just remove them from the input if you're hardcoding the data.
function getBalance(bytes calldata data) public pure returns (uint256 balance) {
(balance) = abi.decode(data, (uint256));
}
// or
function getBalance() public pure returns (uint256 balance) {
bytes memory data = hex'000000000000000000000000278261c4545d65a81ec449945e83a236666b64f5';
(balance) = abi.decode(data, (uint256));
}
Best Answer
You can do it only in assembly.
This is an example using two addresses.
packed
is formed by 32 bytes for length + 20 bytes for first address + 20 bytes for second address. By doingmload(add(packed, ADDRESS_LENGTH))
we end up reading 12+20 bytes, where the last 20 is the first address; since the variablex
is of typeaddress
, the first 12 bytes are automatically dropped. Same reasoning for the second address.