1.solidity实现:
1.1.引入eip712合约:
// SPDX-License-Identifier: MIT
pragma solidity ^ 0.8.0;
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* _Available since v3.4._
*/
abstract contract EIP712 {
/* solhint-disable var-name-mixedcase */
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
// bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
// uint256 private immutable _CACHED_CHAIN_ID;
// bytes32 private immutable _HASHED_NAME;
// bytes32 private immutable _HASHED_VERSION;
// bytes32 private immutable _TYPE_HASH;
/* solhint-enable var-name-mixedcase */
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
// constructor(string memory name, string memory version) {
// bytes32 hashedName = keccak256(bytes(name));
// bytes32 hashedVersion = keccak256(bytes(version));
// bytes32 typeHash = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
// _HASHED_NAME = hashedName;
// _HASHED_VERSION = hashedVersion;
// _CACHED_CHAIN_ID = _getChainId();
// _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
// _TYPE_HASH = typeHash;
// }
bool private eipInitialized;
bytes32 private _CACHED_DOMAIN_SEPARATOR;
uint256 private _CACHED_CHAIN_ID;
bytes32 private _HASHED_NAME;
bytes32 private _HASHED_VERSION;
bytes32 private _TYPE_HASH;
function eip712Initialize(
string memory name,
string memory version
) internal {
require(!eipInitialized, "eipInitialized: Already initialized!");
bytes32 hashedName = keccak256(bytes(name));
bytes32 hashedVersion = keccak256(bytes(version));
bytes32 typeHash = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
_HASHED_NAME = hashedName;
_HASHED_VERSION = hashedVersion;
_CACHED_CHAIN_ID = _getChainId();
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
_TYPE_HASH = typeHash;
eipInitialized = true;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view virtual returns (bytes32) {
if (_getChainId() == _CACHED_CHAIN_ID) {
return _CACHED_DOMAIN_SEPARATOR;
} else {
return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
}
}
function _buildDomainSeparator(bytes32 typeHash, bytes32 name, bytes32 version) private view returns (bytes32) {
return keccak256(
abi.encode(
typeHash,
name,
version,
_getChainId(),
address(this)
)
);
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash));
}
function _getChainId() private view returns (uint256 chainId) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
// solhint-disable-next-line no-inline-assembly
assembly {
chainId := chainid()
}
}
}
1.2.eip712合约初始化:
// 初始化:主要是初始化eip712
function initialize() external onlyAdmin {
require(!initialized, "initialize: Already initialized!");
eip712Initialize("RewardDistributor", "1.0.0");
initialized = true;
}
RewardDistributor是初始化eip712的name,
版本是1.0.0
1.3.eip712自定义初始化结构体:
_hashTypedDataV4(
keccak256(
abi.encode(
keccak256(
"distribute(uint256 season,address to,uint256 uniqueId)"
),
season,
to,
uniqueId
)
)
);
注意:"distribute(uint256 season,address to,uint256 uniqueId)"这个字符串
1. 参数的顺序不能错,season/to/uniqueId
2. 这个字符串中间不能多出一些空格,只有类型后面有空格:"distribute(uint256 season,address to, uint256 uniqueId)"这样就会导致签名不对
2.go实现:
2.1.eip712签名实现:
func SignWithEip712(privateKey *ecdsa.PrivateKey, typedData *apitypes.TypedData) ([]byte, error) {
if privateKey == nil || typedData == nil {
return nil, errors.New("invalid parameter")
}
// 1、获取需要签名的数据的 Keccak-256 的哈希
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
if err != nil {
return nil, err
}
typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
if err != nil {
return nil, err
}
fmt.Printf("\n")
fmt.Printf("domainSeparator: %s\n", hexutil.Encode(domainSeparator))
fmt.Printf("typedDataHash: %s\n", hexutil.Encode(typedDataHash))
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
sigHash := crypto.Keccak256(rawData)
fmt.Printf("rawData: %s\n", hexutil.Encode(rawData))
fmt.Printf("sigHash: %s\n", hexutil.Encode(sigHash))
// 2、使用私钥签名哈希,得到签名
signature, err := crypto.Sign(sigHash, privateKey)
if err != nil {
return nil, err
}
if signature[64] < 27 {
signature[64] += 27
}
return signature, nil
}
2.2.自定义结构体实现:
func MedalNftSigner(
privateKeyStr string,
chainId int64,
contract string,
season int64,
uniqueId int64,
to string,
) (string, error) {
// 签名
typedData := &apitypes.TypedData{
Types: apitypes.Types{
"EIP712Domain": {
{Name: "name", Type: "string"},
{Name: "version", Type: "string"},
{Name: "chainId", Type: "uint256"},
{Name: "verifyingContract", Type: "address"},
},
"distribute": {
{Name: "season", Type: "uint256"},
{Name: "to", Type: "address"},
{Name: "uniqueId", Type: "uint256"},
},
},
Domain: apitypes.TypedDataDomain{
Name: "RewardDistributor",
Version: "1.0.0",
ChainId: math.NewHexOrDecimal256(chainId),
VerifyingContract: contract,
Salt: "",
},
Message: map[string]interface{}{
"season": math.NewHexOrDecimal256(season),
"to": to,
"uniqueId": math.NewHexOrDecimal256(uniqueId),
},
PrimaryType: "distribute",
}
privateKey, err := crypto.HexToECDSA(privateKeyStr)
if err != nil {
return "", err
}
signature, err := SignWithEip712(privateKey, typedData)
if err != nil {
return "", err
}
return hexutil.Encode(signature), nil
}
注意这部分参数的位置要和合约中的一致:
Message: map[string]interface{}{
"season": math.NewHexOrDecimal256(season),
"to": to,
"uniqueId": math.NewHexOrDecimal256(uniqueId),
},
PrimaryType: "distribute",
然后测试的话就没有问题了
标签:name,err,uint256,solidity,bytes32,version,go,调试,string From: https://www.cnblogs.com/zhanchenjin/p/18251490