The domain message is:
struct Offer{
address from;
address to;
uint256 nonce;
uint256[] tokenIds;
uint256 duration;
}
bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
bytes32 constant OFFER_TYPEHASH = keccak256(
"Offer(address from,address to,uint256 nonce,uint256[] memory tokenIds,uint256 duration)"
);
function hash(Offer memory offer) public pure returns (bytes32) {
return keccak256(abi.encode(
OFFER_TYPEHASH,
offer.from,
offer.to,
offer.nonce,
keccak256(abi.encodePacked(offer.tokenIds)),
offer.duration
));
}
function verify(Offer memory offer, uint8 v, bytes32 r, bytes32 s) public view returns (bool) {
// Note: we need to use encodePacked here instead of encode.
bytes32 digest = keccak256(abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
hash(offer)
));
return ecrecover(digest, v, r, s) == ExpectedAddress;
}
I can't resolve issue with array when doing v4 signature.
If I removed tokenIds array from Offer struct, and OfferTypeHash, and also from hash func: everything works well as expected, I can verify signature via web3 and contract.
When I put tokenIds array to struct, I can't verify signature.
I googled and tried a lot of examples like:
I use this for hashing the offer:
keccak256(abi.encodePacked(array))
function hash(Offer memory offer) public pure returns (bytes32) {
return keccak256(abi.encode(
OFFER_TYPEHASH,
offer.from,
offer.to,
offer.nonce,
keccak256(abi.encodePacked(offer.tokenIds)),
offer.duration
));
}
keccak256(abi.encode(array))
keccak256(abi.encodePacked(array))
function hash(Offer memory offer) public pure returns (bytes32) {
return keccak256(abi.encode(
OFFER_TYPEHASH,
offer.from,
offer.to,
offer.nonce,
keccak256(abi.encode(offer.tokenIds)),
offer.duration
));
}
Even without Keccak:
function hash(Offer memory offer) public pure returns (bytes32) {
return keccak256(abi.encode(
OFFER_TYPEHASH,
offer.from,
offer.to,
offer.nonce,
keccak256(abi.encode(offer.tokenIds)),
offer.duration
));
}
I can't figure out: why keccak256(abi.encode is not doing the job?
Related
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.
I have created a solidity that record data CRUD like where I can create read update delete but I want to create a readAll function wondering how can I write in solidity since I write like below it does not work. For calling with id it return the correct but not readAll. Looking forward for your help <3
example
function readAllTask() public view returns (uint, uint256, uint256) {
return (tasks.id, tasks.stackAddress, tasks.nftId); <============= return everything
}
pragma solidity ^0.8.6;
contract RecordData {
struct Task {
uint id;
uint256 stackAddress;
uint256 nftId;
}
Task[] tasks;
uint nextId; // default value 0, add public to see the value
function createTask(uint256 _stackAddress, uint256 _nftId) public {
tasks.push(Task(nextId, _stackAddress, _nftId));
nextId++;
}
function findIndex(uint _id) internal view returns (uint) {
for (uint i = 0; i < tasks.length; i++) {
if (tasks[i].id == _id) {
return i;
}
}
revert("Task not found");
}
function updateTask(uint _id, uint256 _stackAddress, uint256 _nftId) public {
uint index = findIndex(_id);
tasks[index].stackAddress = _stackAddress;
tasks[index].nftId = _nftId;
}
function readTask(uint _id) public view returns (uint, uint256, uint256) {
uint index = findIndex(_id);
return (tasks[index].id, tasks[index].stackAddress, tasks[index].nftId);
}
function deleteTask(uint _id) public {
uint index = findIndex(_id);
delete tasks[index];
}
}
You can return an array of structs:
function readAllTask() public view returns (Task[] memory) {
return tasks;
}
I think you have reason to do it, but just a remind that saving the data into blockchain is not a good deal, due to the high cost.
If you would like to return all the task, then simply make the tasks public. Solidity automatically assign a get function for it.
If you would like to get some specific content of task struct, then consider something like this:
function readAllTask() public view returns (uint, uint256, uint256 [] memory) {
// something
}
I'm trying to get estimated eth needed to perform a swap on ropsten.
interface IUniswap {
function WETH() external pure returns (address);
function getAmountsOut(
uint amountIn,
address[] calldata path)
external
view
returns (uint[] memory amounts);
}
contract UniswapController {
address internal constant UNISWAP_ROUTER_ADDRESS = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
IUniswap public uniswapRouter;
address private UNISWAP_DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
constructor() public {
uniswapRouter = IUniswap(UNISWAP_ROUTER_ADDRESS);
}
function getEstimatedETHforToken(uint daiAmount) public view returns (uint[] memory) {
return uniswapRouter.getAmountsOut(daiAmount, getPathForETHtoDAI());
}
function getWETH() public view returns(address) {
return uniswapRouter.WETH();
}
function getPathForETHtoDAI() public view returns (address[] memory) {
address[] memory path = new address[](2);
path[0] = uniswapRouter.WETH();
path[1] = UNISWAP_DAI_ADDRESS;
return path;
}
}
When I run getEstimatedETHforToken(amount) on remix ide, I get the error:
execution reverted: UniswapV2Library: INSUFFICIENT_LIQUIDITY
I suspect I have the wrong DAI stablecoin address, but this is the address on uniswap's site, which one should I use?
There doesn't seem to be good tokens use on testnets. Just forked the mainnet using ganache-cli and infura with this tutorial and it worked just fine.
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);
}
}
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.