Skip to content

Instantly share code, notes, and snippets.

@benny-conn
Last active October 31, 2023 21:59
Show Gist options
  • Save benny-conn/57c073dab01488f3107d126979ee14fd to your computer and use it in GitHub Desktop.
Save benny-conn/57c073dab01488f3107d126979ee14fd to your computer and use it in GitHub Desktop.
Generating A Signature For Farcaster Signer in Golang
package signer
import (
"context"
"encoding/hex"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
hdwallet "github.com/miguelmota/go-ethereum-hdwallet"
// can be any env library e.g. viper
"env"
)
// response from: POST /v2/farcaster/signer
type NeynarSigner struct {
SignerUUID string `json:"signer_uuid"`
PublicKey string `json:"public_key"`
Status string `json:"status"`
SignerApprovalURL string `json:"signer_approval_url"`
SignerApprovalFID any `json:"fid"`
}
func GenerateSignatureForSigner(ctx context.Context, curSigner NeynarSigner, appFid, deadline *big.Int) ([]byte, error) {
mnemonic := env.GetString("FARCASTER_MNEMONIC")
wallet, err := hdwallet.NewFromMnemonic(mnemonic)
if err != nil {
return nil, err
}
account, err := wallet.Derive(accounts.DefaultBaseDerivationPath, true)
if err != nil {
return nil, err
}
pubBytes, err := hex.DecodeString(strings.TrimPrefix(curSigner.PublicKey, "0x"))
if err != nil {
return nil, err
}
domain := apitypes.TypedDataDomain{
Name: "Farcaster SignedKeyRequestValidator",
Version: "1",
ChainId: math.NewHexOrDecimal256(10),
VerifyingContract: "0x00000000fc700472606ed4fa22623acf62c60553",
}
typedData := apitypes.TypedData{
Types: apitypes.Types{
"EIP712Domain": []apitypes.Type{
{Name: "name", Type: "string"},
{Name: "version", Type: "string"},
{Name: "chainId", Type: "uint256"},
{Name: "verifyingContract", Type: "address"},
},
"SignedKeyRequest": []apitypes.Type{
{Name: "requestFid", Type: "uint256"},
{Name: "key", Type: "bytes"},
{Name: "deadline", Type: "uint256"},
},
},
PrimaryType: "SignedKeyRequest",
Domain: domain,
Message: map[string]interface{}{
"requestFid": appFid.String(),
"key": pubBytes,
"deadline": deadline.String(),
},
}
signature, err := signEIP712TypedData(wallet, account, typedData)
if err != nil {
return nil, err
}
return signature, nil
}
func signEIP712TypedData(wallet *hdwallet.Wallet, account accounts.Account, typedData apitypes.TypedData) ([]byte, error) {
hash, _, err := apitypes.TypedDataAndHash(typedData)
if err != nil {
return nil, err
}
// Sign the final hash
signature, err := wallet.SignHash(account, hash)
if err != nil {
return nil, err
}
signature[64] += 27
return signature, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment