wallet

package
v0.14.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 21, 2024 License: MIT Imports: 11 Imported by: 4

Documentation

Overview

Package wallet contains utilities for importing/using eth wallets.

These are used for importing accounts from various data sources. Currently, key files, seed phrases and private key hexes are supported

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Wallet

type Wallet interface {
	// Address gets the wallets address
	Address() common.Address
	// AddressPtr gets the wallets address as a pointer
	AddressPtr() *common.Address
	// PublicKey returns this wallet's public key as an *ecdsa.PublicKey
	PublicKey() *ecdsa.PublicKey
	// PrivateKey returns this wallet's private key as an *ecdsa.PrivateKey
	PrivateKey() *ecdsa.PrivateKey
	// PublicKeyHex returns this wallet's public key in hex format,
	// without a leading 0x.
	PublicKeyHex() string
	// PrivateKeyHex returns this wallet's private key in hex format,
	// without a leading 0x.
	PrivateKeyHex() string
	// PublicKeyBytes is a helper function that returns the public key as a byte slice
	PublicKeyBytes() []byte
	// PrivateKeyBytes is a helper function that returns the private key as a byte slice
	PrivateKeyBytes() []byte
	// String is an alias for Address().
	String() string
}

Wallet is an EVM-compatible wallet with an ECDSA private key + public key and standard ethereum hex address.

func FromHex

func FromHex(privateKey string) (Wallet, error)

FromHex gets the wallet from the private key.

func FromKeyFile

func FromKeyFile(keyFile string) (Wallet, error)

FromKeyFile creates a wallet from a file. The wallet detects if this is a seed phrase or a private key hex and returns the appropriate wallet. An error is returned if this cannot be determined.

Currently, the default derivation path is used. This should be compatible with metamask and most wallets. It gets the first wallet in this derivation path. TODO: support json files.

func FromPrivateKey

func FromPrivateKey(privKey *ecdsa.PrivateKey) (Wallet, error)

FromPrivateKey creates a new wallet from a private key.

func FromRandom

func FromRandom() (Wallet, error)

FromRandom generates a new private key. Note: this should be used for testing only and has not been audited yet.

func FromSeedPhrase

func FromSeedPhrase(seedPhrase string, derivationPath accounts.DerivationPath) (Wallet, error)

FromSeedPhrase gets the seed phrase for the wallet. Note: there seems to be some issue here w/ longer seed phraeses should investigate.

Example

ExampleFromSeedPhrase shows an example creating a wallet from a seed phrase.

package main

import (
	"encoding/hex"
	"fmt"
	"github.com/Flaque/filet"
	"github.com/brianvoe/gofakeit/v6"
	"github.com/ethereum/go-ethereum/accounts"
	"github.com/ethereum/go-ethereum/common"
	. "github.com/stretchr/testify/assert"
	"github.com/synapsecns/sanguine/ethergo/mocks"
	"github.com/synapsecns/sanguine/ethergo/signer/wallet"
	"github.com/tyler-smith/go-bip39"
	"os"
	"path"
	"strings"
)

// ExampleFromSeedPhrase shows an example creating a wallet from a seed phrase.
func main() {
	const mnemonic = "tag volcano eight thank tide danger coast health above argue embrace heavy"
	testWallet, _ := wallet.FromSeedPhrase(mnemonic, accounts.DefaultBaseDerivationPath)

	fmt.Printf("Address: %s\n", testWallet.Address())
	fmt.Printf("Public Key (From Bytes): %s\n", hex.EncodeToString(testWallet.PrivateKeyBytes()))
	fmt.Printf("Public Key Hex: %s\n", testWallet.PublicKeyHex())
	fmt.Printf("Private Key (From Bytes): %s\n", testWallet.PrivateKeyHex())
	fmt.Printf("Private Key Hex: %s\n", hex.EncodeToString(testWallet.PrivateKeyBytes()))

}

func (s WalletSuite) TestFromKeyFile() {
	for _, testCase := range s.makeKeyFileTests() {
		testFile := s.makeTestFile(testCase.contents, !testCase.shouldErr)
		testWallet, err := wallet.FromKeyFile(testFile)
		if testCase.shouldErr {
			NotNil(s.T(), err)
			continue
		}
		Nil(s.T(), err)

		Equal(s.T(), testWallet.Address(), testCase.address)
	}
}

func (s WalletSuite) TestFromRandom() {
	newWallet, err := wallet.FromRandom()
	Nil(s.T(), err)

	NotPanics(s.T(), func() {
		newWallet.Address()
		newWallet.PrivateKey()
		newWallet.PublicKey()
		newWallet.PrivateKeyBytes()
		newWallet.PublicKeyHex()
		newWallet.PublicKeyBytes()
	})
}

func (s WalletSuite) makeTestFile(contents []byte, fuzz bool) (fileName string) {
	if fuzz {
		contents = fuzzContents(contents)
	}

	testFile, err := os.Create(path.Join(filet.TmpDir(s.T(), ""), fmt.Sprintf("%s.wallet", gofakeit.UUID())))
	Nil(s.T(), err)

	defer func(testFile *os.File) {
		Nil(s.T(), testFile.Close())
	}(testFile)

	_, err = testFile.Write(contents)
	Nil(s.T(), err)

	return testFile.Name()
}

// makeFuzzFile randomly fuzzes a byte slice (representing a file) in a way that should not break
// (e.g. adding a new line at the end, spaces) in a way that does not change the contents.
func fuzzContents(contents []byte) (out []byte) {
	fuzzSetting := gofakeit.Number(0, 3)
	switch fuzzSetting {
	case 0:
		contents = append(contents, []byte(" ")...)
	case 1:
		contents = append(contents, []byte("\n\r")...)
	case 2:
		contents = append(contents, []byte("\f\r")...)
	}
	return contents
}

type KeyFileTest struct {
	// address is the expected output of the address
	address common.Address
	// shouldErr determines whether an error should be expected when running the test
	shouldErr bool
	// contents of the key file
	contents []byte
	// description of what's being tested
	description string
}

func (s WalletSuite) makeKeyFileTests() (tests []KeyFileTest) {
	// test an hdwallet, from https://pkg.go.dev/github.com/miguelmota/go-ethereum-hdwallet#section-readme
	tests = append(tests, KeyFileTest{
		address:     common.HexToAddress("0xC49926C4124cEe1cbA0Ea94Ea31a6c12318df947"),
		shouldErr:   false,
		contents:    []byte("tag volcano eight thank tide danger coast health above argue embrace heavy"),
		description: "test the default derivation path",
	})

	tests = append(tests, KeyFileTest{
		address:     common.HexToAddress("0xC49926C4124cEe1cbA0Ea94Ea31a6c12318df947"),
		shouldErr:   false,
		contents:    []byte("63e21d10fd50155dbba0e7d3f7431a400b84b4c2ac1ee38872f82448fe3ecfb9"),
		description: "test the private key as a hex",
	})

	// fuzz
	tests = append(tests, KeyFileTest{
		address:     mocks.MockAddress(),
		shouldErr:   true,
		contents:    nil,
		description: "no file contents",
	})

	tests = append(tests, KeyFileTest{
		address:     mocks.MockAddress(),
		shouldErr:   true,
		contents:    []byte(strings.Join(bip39.GetWordList(), " ")),
		description: "long word list",
	})

	return tests
}
Output:

Address: 0xC49926C4124cEe1cbA0Ea94Ea31a6c12318df947
Public Key (From Bytes): 63e21d10fd50155dbba0e7d3f7431a400b84b4c2ac1ee38872f82448fe3ecfb9
Public Key Hex: 6005c86a6718f66221713a77073c41291cc3abbfcd03aa4955e9b2b50dbf7f9b6672dad0d46ade61e382f79888a73ea7899d9419becf1d6c9ec2087c1188fa18
Private Key (From Bytes): 63e21d10fd50155dbba0e7d3f7431a400b84b4c2ac1ee38872f82448fe3ecfb9
Private Key Hex: 63e21d10fd50155dbba0e7d3f7431a400b84b4c2ac1ee38872f82448fe3ecfb9

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL