Мне интересно, как правильно определить TYPEHASH для вложенной структуры данных struct для EIP-712. Я пытаюсь сделать это, так как хочу получить подписавшего структуру запроса, используя ECDSA и стандарт EIP-712 для структур хеширования.
Это контракт:
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);
}
}
Я просто хочу убедиться, что правильно кодирую запрос в функции encodeRequest. К сожалению, я не смог найти ничего о том, как создать typehash вложенной структуры. Правильно ли я создаю typehash?
Когда я попробовал функцию проверки без свойства платы и другой TYPEHASH без платы, все сработало совершенно нормально. Однако, когда я пытаюсь получить адрес подписи структуры запроса с массивом сборов, он возвращает неправильный адрес.
Я также видел пример, когда кто-то пытался сделать это:
bytes32 public constant TYPEHASH = keccak256("Request(address to,address from, Fee[] fees)Fee(address recipient, uint256 value)");
К сожалению, он также выдает неверный адрес.





Проведя довольно много исследований (включая чтение всего EIP-712), я смог разработать решение, которое работает:
// 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))
);
}
}
Основная проблема заключалась в том, что для того, чтобы это работало, вам нужно отдельно закодировать каждый элемент Fee внутри структуры Request и хешировать полученный массив, чтобы добавить его к закодированному запросу.