Local ECDSA Signer
The local signer (github.com/smartcontractkit/crec-sdk/transact/signer/local) signs CRE Connect operations with a secp256k1 private key held in process memory. It is the simplest signer and the right choice for local development, integration tests, and CI.
When to use
- Tests / fixtures: deterministic ECDSA signatures.
- CLI tools that read a key from disk or env var.
- Single-node services where the operator owns the key.
For production, prefer a managed signer (AWS KMS, HashiCorp Vault, Fireblocks, Privy) so the key never sits in process memory.
Construct from a private key
import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/smartcontractkit/crec-sdk/transact/signer/local"
)
privateKey, err := crypto.HexToECDSA(os.Getenv("ECDSA_PRIVATE_KEY"))
if err != nil {
return err
}
s := local.NewSigner(privateKey)
NewSigner takes a *ecdsa.PrivateKey (from crypto/ecdsa). The constructor never returns an error: validation happens at signing time.
Generate a fresh key
privateKey, err := crypto.GenerateKey()
if err != nil { return err }
s := local.NewSigner(privateKey)
addr := crypto.PubkeyToAddress(privateKey.PublicKey)
fmt.Println("signer address:", addr.Hex()) // add this to AllowedEcdsaSigners
Load from a hex string
The hex string is the 32-byte secp256k1 private key (no 0x prefix):
// Hardhat/Anvil test key #0: never use in production
privateKey, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")
Sign an operation
opHash, sig, err := client.Transact.SignOperation(ctx, op, s, chainSelector)
local.Signer implements signer.Signer.Sign(ctx, hash) ([]byte, error):
- Calls
crypto.Sign(hash, privateKey)(go-ethereum's secp256k1 sign). - If the recovery byte is
0or1, adds27to make it Ethereum-canonical. - Returns the 65-byte
(r, s, v)signature.
The result is suitable for ecrecover on chain: exactly what the Smart Account verifies.
Provision the wallet's signer set
The signer's address is the keccak256-derived address of its public key:
addr := crypto.PubkeyToAddress(privateKey.PublicKey).Hex()
When you create the wallet, include this address in AllowedEcdsaSigners:
ecdsa := []string{addr}
w, err := client.Wallets.Create(ctx, wallets.CreateInput{
Name: "dev",
ChainSelector: "16015286601757825753",
WalletOwnerAddress: ownerEOA.Hex(),
WalletType: apiClient.Ecdsa,
AllowedEcdsaSigners: &ecdsa,
StatusChannelId: &statusChannelID, // optional; receives wallet.status events
})
Security checklist
- Inject the key via env var; never check it into source control.
- Bind the key to a wallet whose blast radius is bounded (test funds, low-value testnet positions).
- Rotate the key by archiving the wallet and provisioning a new one with the new signer; see Manage Wallet Signers.
Next steps
- AWS KMS Signer: production-grade equivalent.
- Build and Sign Operations: feed the signer into the operation flow.