package util_test import ( "github.com/brianvoe/gofakeit/v6" "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/assert" "github.com/synapsecns/sanguine/core" "github.com/synapsecns/sanguine/core/testsuite" "github.com/synapsecns/sanguine/ethergo/mocks" "github.com/synapsecns/sanguine/ethergo/util" "math/big" "testing" ) // nolint: cyclop, gocognit func (u *UtilSuite) TestCopyTX() { mockDynamicTXS := mocks.GetMockTxes(u.GetTestContext(), u.T(), 100, types.DynamicFeeTxType) mockLegacyTXes := mocks.GetMockTxes(u.GetTestContext(), u.T(), 100, types.LegacyTxType) // delete me mockTXes := append(mockDynamicTXS, mockLegacyTXes...) for _, tx := range mockTXes { // fuzz some data txFuzzData := makeFuzzData(u.T(), tx) txFuzzData.txType = types.DynamicFeeTxType // delete me // build options var options []util.CopyOption if txFuzzData.nonce != tx.Nonce() { options = append(options, util.WithNonce(txFuzzData.nonce)) } if txFuzzData.gasPrice != nil && txFuzzData.gasPrice.Cmp(tx.GasPrice()) != 0 && tx.Type() == types.LegacyTxType { options = append(options, util.WithGasPrice(txFuzzData.gasPrice)) } if txFuzzData.feeCap != nil && txFuzzData.feeCap.Cmp(tx.GasFeeCap()) != 0 && tx.Type() == types.DynamicFeeTxType { options = append(options, util.WithGasFeeCap(txFuzzData.feeCap)) } if txFuzzData.tipCap != nil && txFuzzData.tipCap.Cmp(tx.GasTipCap()) != 0 && tx.Type() == types.DynamicFeeTxType { options = append(options, util.WithGasTipCap(txFuzzData.tipCap)) } if txFuzzData.txType != tx.Type() { options = append(options, util.WithTxType(txFuzzData.txType)) } // copy the tx with the options newTx, err := util.CopyTX(tx, options...) u.Require().NoError(err) ogV, ogR, ogS := tx.RawSignatureValues() newV, newR, newS := newTx.RawSignatureValues() // make sure pointers changed assert.False(u.T(), core.ArePointersEqual(tx.ChainId(), newTx.ChainId())) assert.False(u.T(), core.ArePointersEqual(tx.Nonce(), newTx.Nonce())) assert.False(u.T(), core.ArePointersEqual(tx.GasPrice(), newTx.GasPrice())) assert.False(u.T(), core.ArePointersEqual(tx.GasTipCap(), newTx.GasTipCap())) assert.False(u.T(), core.ArePointersEqual(tx.GasFeeCap(), newTx.GasFeeCap())) assert.False(u.T(), core.ArePointersEqual(ogV, newV)) assert.False(u.T(), core.ArePointersEqual(ogR, newR)) assert.False(u.T(), core.ArePointersEqual(ogS, newS)) // make sure fields without options were not changed assert.Equal(u.T(), tx.ChainId(), newTx.ChainId(), testsuite.BigIntComparer()) assert.Equal(u.T(), tx.To(), newTx.To()) assert.Equal(u.T(), tx.Value(), newTx.Value(), testsuite.BigIntComparer()) assert.Equal(u.T(), tx.Data(), newTx.Data(), testsuite.BigIntComparer()) // they won't be equal in this case if tx.Type() == txFuzzData.txType { assert.Equal(u.T(), tx.Type(), newTx.Type()) } // make sure new tx matches target assert.Equal(u.T(), newTx.Type(), txFuzzData.txType) assert.Equal(u.T(), tx.Gas(), newTx.Gas()) // don't check the v param if we've changed it to return the chainid correctly if tx.Type() == txFuzzData.txType { assert.Equal(u.T(), ogV, newV, testsuite.BigIntComparer()) // this wil change slightly, it's not a problem assert.Equal(u.T(), tx.AccessList(), newTx.AccessList()) // make sure the newTX matches the fuzz data if !txFuzzData.matchesTX(newTx) { u.T().Errorf("expected tx to match fuzz data, but it did not. tx: %v, fuzzData: %v", newTx, txFuzzData) } } assert.Equal(u.T(), ogR, newR, testsuite.BigIntComparer()) assert.Equal(u.T(), ogS, newS, testsuite.BigIntComparer()) } } // nolint: gocognit, cyclop func makeFuzzData(tb testing.TB, tx *types.Transaction) fuzzData { tb.Helper() txType := tx.Type() nonce := tx.Nonce() if gofakeit.Bool() { nonce = gofakeit.Uint64() } var gasPrice, feeCap, tipCap *big.Int if tx.Type() == types.DynamicFeeTxType { feeCap = tx.GasFeeCap() if gofakeit.Bool() { feeCap = new(big.Int).SetUint64(gofakeit.Uint64()) } tipCap = tx.GasTipCap() if gofakeit.Bool() { tipCap = new(big.Int).SetUint64(gofakeit.Uint64()) } } if tx.Type() == types.LegacyTxType { gasPrice = tx.GasPrice() if gofakeit.Bool() { gasPrice = new(big.Int).SetUint64(gofakeit.Uint64()) } } if gofakeit.Bool() { var err error txTypes := []uint8{types.DynamicFeeTxType, types.LegacyTxType} txType, err = core.RandomItem[uint8](txTypes) assert.Nilf(tb, err, "failed to get random tx type: %v", err) } return fuzzData{ nonce: nonce, gasPrice: gasPrice, feeCap: feeCap, tipCap: tipCap, txType: txType, } } type fuzzData struct { nonce uint64 gasPrice *big.Int feeCap *big.Int tipCap *big.Int txType uint8 } func (f *fuzzData) matchesTX(tx *types.Transaction) bool { if f.nonce != tx.Nonce() { return false } if f.gasPrice != nil && f.gasPrice.Cmp(tx.GasPrice()) != 0 { return false } if f.txType == tx.Type() { if f.feeCap != nil && f.feeCap.Cmp(tx.GasFeeCap()) != 0 { return false } if f.tipCap != nil && f.tipCap.Cmp(tx.GasTipCap()) != 0 { return false } } return true } func TestMakeOptionsNil(t *testing.T) { opts := util.MakeOptions() if opts.Nonce() != nil { t.Errorf("expected nonce to be nil, got %v", opts.Nonce()) } if opts.GasPrice() != nil { t.Errorf("expected gas price to be nil, got %v", opts.GasPrice()) } if opts.GasFeeCap() != nil { t.Errorf("expected gas fee cap to be nil, got %v", opts.GasFeeCap()) } if opts.GasTipCap() != nil { t.Errorf("expected gas tip cap to be nil, got %v", opts.GasTipCap()) } } func TestMakeOptionsWithNonce(t *testing.T) { opts := util.MakeOptions(util.WithNonce(1234)) if opts.Nonce() == nil { t.Errorf("expected nonce to be set, got nil") } if *opts.Nonce() != 1234 { t.Errorf("expected nonce to be 1234, got %v", *opts.Nonce()) } if opts.GasPrice() != nil { t.Errorf("expected gas price to be nil, got %v", opts.GasPrice()) } if opts.GasFeeCap() != nil { t.Errorf("expected gas fee cap to be nil, got %v", opts.GasFeeCap()) } if opts.GasTipCap() != nil { t.Errorf("expected gas tip cap to be nil, got %v", opts.GasTipCap()) } } func TestMakeOptionsWithTxType(t *testing.T) { txTypes := []uint8{types.LegacyTxType, types.DynamicFeeTxType, types.AccessListTxType} for _, txType := range txTypes { opts := util.MakeOptions(util.WithTxType(txType)) if opts.Nonce() != nil { t.Errorf("expected nonce to not be set, got %v", opts.Nonce()) } if opts.TxType() == nil { t.Errorf("expected tx type to be set, got nil") } if *opts.TxType() != txType { t.Errorf("expected tx type to be %v, got %v", txType, *opts.TxType()) } if opts.GasPrice() != nil { t.Errorf("expected gas price to be nil, got %v", opts.GasPrice()) } if opts.GasFeeCap() != nil { t.Errorf("expected gas fee cap to be nil, got %v", opts.GasFeeCap()) } if opts.GasTipCap() != nil { t.Errorf("expected gas tip cap to be nil, got %v", opts.GasTipCap()) } } } func TestMakeOptionsWithGasPrice(t *testing.T) { gasPrice := big.NewInt(100) opts := util.MakeOptions(util.WithGasPrice(gasPrice)) if opts.Nonce() != nil { t.Errorf("expected nonce to be nil, got %v", opts.Nonce()) } if opts.GasPrice() == nil { t.Errorf("expected gas price to be set, got nil") } if opts.GasPrice().Cmp(gasPrice) != 0 { t.Errorf("expected gas price to be %v, got %v", gasPrice, opts.GasPrice()) } if opts.GasFeeCap() != nil { t.Errorf("expected gas fee cap to be nil, got %v", opts.GasFeeCap()) } if opts.GasTipCap() != nil { t.Errorf("expected gas tip cap to be nil, got %v", opts.GasTipCap()) } } func TestMakeOptionsWithGasFeeCap(t *testing.T) { gasFeeCap := big.NewInt(200) gasTipCap := big.NewInt(300) opts := util.MakeOptions(util.WithGasFeeCap(gasFeeCap), util.WithGasTipCap(gasTipCap)) if opts.Nonce() != nil { t.Errorf("expected nonce to be nil, got %v", opts.Nonce()) } if opts.GasPrice() != nil { t.Errorf("expected gas price to be nil, got %v", opts.GasPrice()) } if opts.GasFeeCap() == nil { t.Errorf("expected gas fee cap to be set, got nil") } if opts.GasFeeCap().Cmp(gasFeeCap) != 0 { t.Errorf("expected gas fee cap to be %v, got %v", gasFeeCap, opts.GasFeeCap()) } if opts.GasTipCap() != gasTipCap { t.Errorf("expected gas tip cap to be nil, got %v", opts.GasTipCap()) } }