Skip to content

Commit

Permalink
simplify privatekey_wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
samlaf committed Jul 24, 2024
1 parent e720106 commit bb33f19
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 31 deletions.
41 changes: 12 additions & 29 deletions chainio/clients/wallet/privatekey_wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,29 @@ package wallet
import (
"context"
"fmt"
"math/big"

"github.com/Layr-Labs/eigensdk-go/chainio/clients/eth"
"github.com/Layr-Labs/eigensdk-go/logging"
"github.com/Layr-Labs/eigensdk-go/signerv2"
"github.com/Layr-Labs/eigensdk-go/utils"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)

var _ Wallet = (*privateKeyWallet)(nil)
type EthBackend interface {
SendTransaction(ctx context.Context, tx *types.Transaction) error
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
}

type privateKeyWallet struct {
ethClient eth.Client
ethClient EthBackend
address common.Address
signerFn signerv2.SignerFn
logger logging.Logger

// cache
contracts map[common.Address]*bind.BoundContract
}

var _ Wallet = (*privateKeyWallet)(nil)

func NewPrivateKeyWallet(
ethClient eth.Client,
signer signerv2.SignerFn,
Expand All @@ -38,7 +37,6 @@ func NewPrivateKeyWallet(
address: signerAddress,
signerFn: signer,
logger: logger,
contracts: make(map[common.Address]*bind.BoundContract, 0),
}, nil
}

Expand All @@ -51,32 +49,17 @@ func (t *privateKeyWallet) SendTransaction(ctx context.Context, tx *types.Transa
}

t.logger.Debug("Sending transaction")
opts := &bind.TransactOpts{
From: t.address,
Nonce: new(big.Int).SetUint64(tx.Nonce()),
Signer: signer,
Value: tx.Value(),
GasFeeCap: tx.GasFeeCap(),
GasTipCap: tx.GasTipCap(),
GasLimit: tx.Gas(),
Context: ctx,
}

contract := t.contracts[*tx.To()]
// if the contract has not been cached
if contract == nil {
// create a dummy bound contract tied to the `to` address of the transaction
contract = bind.NewBoundContract(*tx.To(), abi.ABI{}, t.ethClient, t.ethClient, t.ethClient)
// cache the contract for later use
t.contracts[*tx.To()] = contract
signedTx, err := signer(t.address, tx)
if err != nil {
return "", utils.WrapError(fmt.Errorf("sign: tx %v failed.", tx.Hash().String()), err)
}

sendingTx, err := contract.RawTransact(opts, tx.Data())
err = t.ethClient.SendTransaction(ctx, signedTx)
if err != nil {
return "", utils.WrapError(fmt.Errorf("send: tx %v failed.", tx.Hash().String()), err)
}

return sendingTx.Hash().Hex(), nil
return signedTx.Hash().Hex(), nil
}

func (t *privateKeyWallet) GetTransactionReceipt(ctx context.Context, txID TxID) (*types.Receipt, error) {
Expand Down
70 changes: 70 additions & 0 deletions chainio/clients/wallet/privatekey_wallet_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package wallet

import (
"context"
"math/big"
"testing"
"time"

"github.com/Layr-Labs/eigensdk-go/chainio/clients/eth"
"github.com/Layr-Labs/eigensdk-go/signerv2"
"github.com/Layr-Labs/eigensdk-go/testutils"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
)

var (
chainId = big.NewInt(31337)
)

func TestPrivateKeyWallet(t *testing.T) {
logger := testutils.NewTestLogger()

t.Run("SendTransaction + GetTransactionReceipt", func(t *testing.T) {
anvilC, err := testutils.StartAnvilContainer("")
require.NoError(t, err)
ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
anvilHttpEndpoint, err := anvilC.Endpoint(ctxWithTimeout, "http")
require.NoError(t, err)
ethClient, err := eth.NewClient(anvilHttpEndpoint)
require.NoError(t, err)

ecdsaPrivKeyHex := "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
ecdsaPrivKey, err := crypto.HexToECDSA(ecdsaPrivKeyHex)
require.NoError(t, err)
signerV2, signerAddr, err := signerv2.SignerFromConfig(signerv2.Config{PrivateKey: ecdsaPrivKey}, chainId)
if err != nil {
panic(err)
}

skWallet, err := NewPrivateKeyWallet(ethClient, signerV2, signerAddr, logger)
require.NoError(t, err)

tx := types.NewTx(&types.DynamicFeeTx{
ChainID: chainId,
Nonce: 0,
GasTipCap: big.NewInt(1),
GasFeeCap: big.NewInt(1_000_000_000),
Gas: 21000,
To: &signerAddr,
Value: big.NewInt(1),
})
ctxWithTimeout, cancel = context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
txId, err := skWallet.SendTransaction(ctxWithTimeout, tx)
require.NoError(t, err)

// need to give some time for anvil to process the tx and mine the block
// TODO: shall we expose a public WaitForTxReceipt function in the wallet interface, or somewhere else?
time.Sleep(3 * time.Second)

ctxWithTimeout, cancel = context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
receipt, err := skWallet.GetTransactionReceipt(ctxWithTimeout, txId)
require.NoError(t, err)
// make sure the txHash in the mined tx receipt matches the once we sent
require.Equal(t, txId, receipt.TxHash.String())
})
}
4 changes: 3 additions & 1 deletion testutils/anvil.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ func StartAnvilContainer(anvilStateFileName string) (testcontainers.Container, e
// Still need to advance the chain by at least 1 block b/c some tests need to query the latest block,
// and the blocks dumped/loaded by anvil don't contain full transactions, which leads to panics in tests.
// See https://github.com/foundry-rs/foundry/issues/8213, which will hopefully get fixed soon.
AdvanceChainByNBlocksExecInContainer(ctx, 1, anvilC)
if anvilStateFileName != "" {
AdvanceChainByNBlocksExecInContainer(ctx, 1, anvilC)
}

return anvilC, nil
}
Expand Down
3 changes: 2 additions & 1 deletion testutils/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ import (
// NewTestLogger is just a utility function to create a logger for testing
// It returns an slog textHandler logger that outputs to os.Stdout, with source code information and debug level
func NewTestLogger() logging.Logger {
return logging.NewTextSLogger(os.Stdout, &logging.SLoggerOptions{AddSource: true, Level: slog.LevelDebug})
// we don't use colors because the test output panel in vscode doesn't support them
return logging.NewTextSLogger(os.Stdout, &logging.SLoggerOptions{AddSource: true, Level: slog.LevelDebug, NoColor: true})
}

0 comments on commit bb33f19

Please sign in to comment.