I'm trying to generate a signature in line with https://eips.ethereum.org/EIPS/eip-712
so that I can permit spending of tokens without having to call Approve() first.
this is my code so far:
func GetPermitHashVars(permitTypeHash, domainSeparator [32]byte, owner, spender common.Address, amount, nonce, deadline *big.Int, pk *ecdsa.PrivateKey) (byte, [32]byte, [32]byte, error) {
// Calculate the permit hash
var message []byte
message = append(message, byte(0x19)) // EIP-191 header
message = append(message, byte(0x01)) // EIP-191 version
// Append the domainSeparator
for _, b := range domainSeparator {
message = append(message, b)
}
// Append the permitTypeHash
for _, b := range permitTypeHash {
message = append(message, b)
}
// Append the owner, spender, value, nonce, and deadline
message = append(message, owner.Bytes()...)
message = append(message, spender.Bytes()...)
message = append(message, amount.Bytes()...)
message = append(message, nonce.Bytes()...)
message = append(message, deadline.Bytes()...)
digest := crypto.Keccak256(message)
sig, err := crypto.Sign(digest, pk)
if err != nil {
return 0, [32]byte{}, [32]byte{}, err
}
var v byte
var r [32]byte
var s [32]byte
v = sig[64] + 27
copy(r[:], sig[:32])
copy(s[:], sig[32:64])
return v, r, s, nil
}
I keep getting an invalid signature error from the contract and I can't work out why, any ideas would be much appreciated!
UPDATE:
I've edited the code to try a different approach, but same error! any ideas? i'm really lost on this one.
func GetPermitHashVars(name string, owner, spender, pair common.Address, amount, nonce, deadline, chainId *big.Int, pk *ecdsa.PrivateKey) (byte, [32]byte, [32]byte, error) {
var typesStandard = apitypes.Types{
"EIP712Domain": {
{
Name: "name",
Type: "string",
},
{
Name: "version",
Type: "string",
},
{
Name: "chainId",
Type: "uint256",
},
{
Name: "verifyingContract",
Type: "address",
},
},
"Permit": {
{
Name: "owner",
Type: "address",
},
{
Name: "spender",
Type: "address",
},
{
Name: "value",
Type: "uint256",
},
{
Name: "nonce",
Type: "uint256",
},
{
Name: "deadline",
Type: "uint256",
},
},
}
var domainStandard = apitypes.TypedDataDomain{
Name: name,
Version: "1",
ChainId: math.NewHexOrDecimal256(chainId.Int64()),
VerifyingContract: pair.Hex(),
}
typedData := apitypes.TypedData{
Domain: domainStandard,
PrimaryType: "Permit",
Types: typesStandard,
Message: apitypes.TypedDataMessage{"owner": owner.Hex(), "spender": spender.Hex(), "value": amount, "nonce": nonce, "deadline": deadline},
}
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
if err != nil {
return 0, [32]byte{}, [32]byte{}, err
}
typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
if err != nil {
return 0, [32]byte{}, [32]byte{}, err
}
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
hashed := crypto.Keccak256(rawData)
sig, err := crypto.Sign(hashed, pk)
if err != nil {
return 0, [32]byte{}, [32]byte{}, err
}
fmt.Println(len(sig))
var v byte
var r [32]byte
var s [32]byte
v = sig[64] + 27
copy(r[:], sig[:32])
copy(s[:], sig[32:64])
return v, r, s, nil
}