我正在使用醚的_signTypedData()方法对类型化数据进行签名。但是当我使用ECDSA.recover()在Smart契约中检索签名者的地址时,它会返回错误的地址。如果我错过了什么或者采取了错误的方法,请告诉我。我真的很感谢你的帮助!
前端代码
let token = {
tokenId: 1,
account: "<< my_wallet_address >>"
};
const provider = new ethers.providers.Web3Provider(window.ethereum);
let signature;
let oContract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider);
$("#btnSignTransaction").on("click", async function () {
const signer = provider.getSigner();
const {
chainId
} = await provider.getNetwork();
signature = await signer._signTypedData({
name: 'Name',
version: '1.0.0',
chainId: chainId,
verifyingContract: CONTRACT_ADDRESS,
}, {
NFT: [{
name: 'tokenId',
type: 'uint256'
},
{
name: 'account',
type: 'address'
},
],
},
token
);
token.signature = signature;
});
$("#btnCall").on("click", async function () {
const addressFromContract = await oContract.getAccountAddress(token.account, token.tokenId, token.signature);
console.log(addressFromContract);
});实心码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract ERC721LazyMintWith712 is ERC721, EIP712, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
constructor(string memory name, string memory symbol)
ERC721(name, symbol)
EIP712(name, "1.0.0")
{
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
function _hash(address account, uint256 tokenId)
internal
view
returns (bytes32)
{
return
_hashTypedDataV4(
keccak256(
abi.encode(
keccak256("NFT(uint256 tokenId,address account)"),
tokenId,
account
)
)
);
}
function _verify(bytes32 digest, bytes memory signature)
internal
view
returns (bool)
{
return hasRole(MINTER_ROLE, ECDSA.recover(digest, signature));
}
function getAccountAddress(address account, uint256 tokenId, bytes calldata signature) external view returns(address) {
return ECDSA.recover(_hash(account, tokenId), signature);
}
}我正在尝试实现来自这回购公司的ERC721LazyMintWith712.solSmart合同。然而,当我通过创建测试用例来测试合同时,它工作得很完美,但是,对于前端集成,它却没有!
发布于 2022-03-23 21:16:39
我无意中发现了这个问题,并浏览了Ethers.js代码和草案- you 712.sol代码,但结果始终与您相同。
代码是完全正确的,并遵守EIP-712。我最好的猜测是,您可能使用了一个测试块链环境,比如ganache /ganache,它具有非常特殊的行为:
用这一行:
const { chainId } = await provider.getNetwork();实际上,您得到的是networkId,而不是草案- are 712.sol通过block.chainid获得的chainId。因此,不同的Ids导致不同的数据、不同的散列,从而导致不同的签名。
虽然您可以在chainId代码中硬编码1,但最干净的解决方案是在JavaScript上使用--chainId X标志,其中X可以被您选择的chainId (即1337)替换,它将确保RPC chainId和Opcode chainID都是相同的值。
使用ganache-cli --chainId 1337,您的代码可以很好地工作在Remix或MetaMask中,默认情况下,chainId和networkId在RPC和Opcode之间是一致的。
使用问题的第二个正在进行的MetaMask (我假设您正在使用它)是,默认的http://localhost:8545端点假定有一个1337的chainId,这可能不适用于硬帽子或特定的ganache配置,这也可能导致客户端的错误签名,但也应该禁止发送任何tx。要么将节点设置为1337的chainId,要么创建自己的MetaMask端点。
所以你的代码没问题。这无疑是一个配置问题,无论是在MetaMask / Ganache还是如上面所述的硬帽子中。
https://ethereum.stackexchange.com/questions/109449
复制相似问题