How to encode tuple as input parameter to function using web3j - ethereum

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.

Related

Ordinal Number Function in Remix Solidity

I want to create a new function based on these codes below, that it can just fill in the ordinal number (first student is started by 1) to track out the struct student info in this case.
That's mean after I give the data of this first student to the struct by addInfo function:["Simon", 20, "CA USA", 10] Then my expected new function is that I just fill in this unit: 1, then it will appear this student detail
\`
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ReferenceTypes {
address public owner;
struct student {
string Name;
uint Age;
string BirthPlace;
uint8 Marks;
}
constructor() {
owner = msg.sender;
}
mapping (address => student) public Info;
address[] public student_Info;
function addInfo(student memory _student, address _address) public {
require(owner == msg.sender, "Only admin can add Info!!!");
Info[_address] = _student;
student_Info.push(_address);
}
function count() public view returns(uint) {
return student_Info.length;
}
}
\`
Thanks so much guys
I tried a new mapping but failed
You can make another mapping that leads to your mapping Info.
uint public studentsCounter;
mapping (uint => mapping (address => student)) public Info;
function addInfo(student memory _student, address _address) public returns(uint){
require(owner == msg.sender, "Only admin can add Info!!!");
Info[studentsCounter][_address] = _student;
studentsCounter++;
student_Info.push(_address);
return studentsCounter-1;
}
Thus you can reach your struct by passing studentId(studentCounter) which addInfo returns and students address.
Another approach is to remake you array of addresses.
student[] public student_Info;
And push struct in this array.
function addInfo(student memory _student, address _address) public {
require(owner == msg.sender, "Only admin can add Info!!!");
Info[_address] = _student;
student_Info.push(_student);
}
So now you can display studentsInfo buy passing index in student_Info array.

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 to instantiate functions of other smart contracts to write and read data from the same contract on Solidity?

I'm trying to understand inheritance, interfaces and the call of functions from other smart contracts in Solidity. So I did this example. It has two interfaces, one for struct type definition and other for functions definition. It also has two contracts for implementing logic of functions defined in the interface. Finally, there is a main contract that interacts with functions contracts via the interface.
My question is how to share data written and read through the contract, since when interacting with functions contracts on the main smart contract, the struct data type that is written in registerPerson function is not read on getPerson function. It returns me a blank struct data type.
I think maybe is something that's missing in the code, but I don't know what it is.
Thanks in advance.
StructInterface interface Smart Contract
interface StructInterface {
struct Person {
uint personId;
string name;
string surname;
uint age;
}
}
FunctionsInterface interface Smart Contract
interface FunctionsInterface {
function addPerson (uint, string memory, string memory,uint ) external;
function askPerson(uint) external view returns (uint, string memory, string memory, uint);
}
RegisterPerson Smart Contract
import "./StructInterface.sol";
contract RegisterPerson is StructInterface {
mapping (uint => Person) persons;
function addPerson (
uint _personId,
string memory _name,
string memory _surname,
uint _age
) external {
persons[_personId].personId = _personId;
persons[_personId].name = _name;
persons[_personId].surname =_surname;
persons[_personId].age = _age;
}
}
GetPerson Smart Contract
import "./StructInterface.sol";
contract GetPerson is StructInterface {
mapping (uint => Person) persons;
function askPerson(uint _personId) external view
returns (uint personId, string memory name, string memory surname, uint age) {
personId = persons[_personId].personId;
name = persons[_personId].name;
surname = persons[_personId].surname;
age = persons[_personId].age;
return (personId, name, surname, age);
}
}
Main Smart Contract
import "./StructInterface.sol";
import "./RegisterPerson.sol";
import "./GetPerson.sol";
import "./FunctionsInterface.sol";
contract Main is StructInterface{
address regPersonA;
address getPersonA;
Person myPersons;
constructor (address _regPersonA, address _getPersonA) {
regPersonA = _regPersonA;
getPersonA = _getPersonA;
}
function registerPerson(
uint _personId,
string memory _name,
string memory _surname,
uint _age
) external {
FunctionsInterface interf = FunctionsInterface(regPersonA);
interf.addPerson(_personId, _name, _surname, _age);
}
function getPerson(
uint _personId
) external view
returns (uint personId, string memory name, string memory surname, uint age)
{
FunctionsInterface interf = FunctionsInterface(getPersonA);
return interf.askPerson(_personId);
}
}

Return value from a deployed smart contract, via a smart contract, to a smart contract

I am trying to return a value using a function of a deployed smart contract on the blockchain.
pragma solidity 0.6.2;
contract Caller {
address cont;
function changeAdd(address _change) public {
cont = _change;
}
function caller (bytes memory test) public returns(bool, bytes memory) {
bytes memory payload = abi.encodeWithSignature("callMe(bytes)", test);
(bool success, bytes memory result)= address(cont).call(payload);
return (success, (result));
}
function viewCont() public view returns(address) {
return cont;
}
}
And the deployed test contract is this:
pragma solidity 0.6.2;
contract Store {
uint counter;
function callMe(bytes calldata test) external returns(bytes memory) {
counter++;
return abi.encode(test);
}
function viewCounter () public view returns(uint256) {
return(counter);
}
function clearCounter() public {
counter = 0 ;
}
}
In this example, I am deploying contract "Store" on the blockchain where it is assigned a random address, which can be pointed at via the Caller.changeAdd function in order to use Caller.caller function. Thus, I am trying to send data in the form of bytes to the Store contract, which is supposed to send back the same data to the Caller contract. I am trying to isolate -only- the data that is originally sent, so I can use it to test interaction between smart contracts on the blockchain. I tried in the beginning to send an integer, but I couldn't find a way to do it. So I used bytes and it worked, but still the data I receive isn't the same that I send in the first place(e.g. I send 0x0 and I receive a big bytes number, which isn't the same as 0x0).
I could appreciate any help on how to receive and handle data between two different, unlinked smart contracts, thank you in advance.
Do you try to use abi.decode function of Solidity.
In the below example, contract B calls setName of contract A, then decodes the result by using abi.decode function.
contract A {
string public name;
constructor(string memory tokenName) public {
name = tokenName;
}
function setName(string memory newName) public returns ( string memory){
name = newName;
return newName;
}
}
contract B {
event Log(string msg);
string public myName;
function call(address addr, string memory newName) public {
bytes memory payload = abi.encodeWithSignature("setName(string)", newName);
(bool success, bytes memory result)= addr.call(payload);
// Decode data
string memory name = abi.decode(result, (string));
myName = name;
emit Log(name);
}
}

Geth + smart contract function = bad instruction

playing with ethereum smart contracts I encountered a following problem. The contract gets deployed on Ropsten and then I'm trying to call function 'addRecipe' with the following code on geth:
recipes.addRecipe(300, "zupa", "zupa z trupa", {from:web3.eth.accounts[0], gas: 20000000})
The function looks as following:
function addRecipe(uint256 _price, string _name, string _content) public {
recipes[recipeCount].price = _price;
recipes[recipeCount].name = _name;
recipes[recipeCount].content = _content;
recipes[recipeCount].recipeOwner = msg.sender;
recipeCount++;
}
I get the TX hash but looking up the transaction in Etherscan gives
Warning! Error encountered during contract execution [Bad instruction]
You can check it up here:
https://ropsten.etherscan.io/tx/0xe5999c2d122e4871e82f5986397dfd39107cee2056a9280132abeaa460c0f66d
Adding 'payable' modifier to the function or using the following command doesn't give any better results...
recipes.addRecipe.sendTransaction(300, "zupa", "zupa z trupa", {from:web3.eth.accounts[0], gas: 200000000})
The whole contract:
pragma solidity ^0.4.24;
contract Recipes {
address owner;
uint256 recipeCount = 0;
struct Recipe {
string name;
string content;
uint256 price;
address recipeOwner;
}
Recipe[] public recipes;
function () public {
owner = msg.sender;
}
function kill() public {
require (msg.sender == owner);
selfdestruct(owner);
}
function addRecipe(uint256 _price, string _name, string _content) public {
recipes[recipeCount].price = _price;
recipes[recipeCount].name = _name;
recipes[recipeCount].content = _content;
recipes[recipeCount].recipeOwner = msg.sender;
recipeCount++;
}
function showRecipes(uint256 _id) constant returns(string) {
return recipes[_id].content;
}
}
recipes is a dynamic storage array. You need to change the size of the array to add new elements to it. You can either do this by explicitly increasing the length of the array or by pushing a new element into the array.
function addRecipe(uint256 _price, string _name, string _content) public {
recipes.length++;
recipes[recipeCount].price = _price;
recipes[recipeCount].name = _name;
recipes[recipeCount].content = _content;
recipes[recipeCount].recipeOwner = msg.sender;
recipeCount++;
}
Or
function addRecipe(uint256 _price, string _name, string _content) public {
Recipe memory r;
r.price = _price;
r.name = _name;
r.content = _content;
r.recipeOwner = msg.sender;
recipes.push(r);
recipeCount++;
}