454 lines
13 KiB
Go
454 lines
13 KiB
Go
package arbitrum
|
|
|
|
import (
|
|
"math/big"
|
|
"testing"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/fraktal/mev-beta/internal/logger"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// TestCalculateL1DataFee tests L1 data fee calculation
|
|
func TestCalculateL1DataFee(t *testing.T) {
|
|
logger := logger.New("info", "text", "")
|
|
|
|
// Test with nil calldata
|
|
fee := CalculateL1DataFee(nil, logger)
|
|
assert.Equal(t, big.NewInt(0), fee)
|
|
|
|
// Test with empty calldata
|
|
fee = CalculateL1DataFee([]byte{}, logger)
|
|
assert.Equal(t, big.NewInt(0), fee)
|
|
|
|
// Test with sample calldata
|
|
calldata := []byte("hello world")
|
|
fee = CalculateL1DataFee(calldata, logger)
|
|
assert.NotNil(t, fee)
|
|
assert.True(t, fee.Sign() >= 0)
|
|
|
|
// Test with longer calldata
|
|
longCalldata := make([]byte, 1000)
|
|
for i := range longCalldata {
|
|
longCalldata[i] = byte(i % 256)
|
|
}
|
|
fee = CalculateL1DataFee(longCalldata, logger)
|
|
assert.NotNil(t, fee)
|
|
assert.True(t, fee.Sign() >= 0)
|
|
|
|
// Fee for longer calldata should be higher than for shorter calldata
|
|
shortFee := CalculateL1DataFee(calldata, logger)
|
|
assert.True(t, fee.Cmp(shortFee) >= 0)
|
|
}
|
|
|
|
// TestCalculateL2Gas tests L2 gas calculation
|
|
func TestCalculateL2Gas(t *testing.T) {
|
|
logger := logger.New("info", "text", "")
|
|
|
|
// Test with nil transaction
|
|
gas := CalculateL2Gas(nil, logger)
|
|
assert.Equal(t, big.NewInt(0), gas)
|
|
|
|
// Test with transaction with gas limit
|
|
tx := &RPCTransaction{
|
|
Gas: "0x5208", // 21000 in decimal
|
|
}
|
|
gas = CalculateL2Gas(tx, logger)
|
|
assert.Equal(t, big.NewInt(21000), gas)
|
|
|
|
// Test with transaction with zero gas limit
|
|
txZero := &RPCTransaction{
|
|
Gas: "0x0",
|
|
}
|
|
gas = CalculateL2Gas(txZero, logger)
|
|
assert.Equal(t, big.NewInt(0), gas)
|
|
|
|
// Test with malformed gas field
|
|
txMalformed := &RPCTransaction{
|
|
Gas: "invalid_hex",
|
|
}
|
|
gas = CalculateL2Gas(txMalformed, logger)
|
|
assert.Equal(t, big.NewInt(0), gas)
|
|
}
|
|
|
|
// TestEstimateTotalGasCost tests total gas cost estimation
|
|
func TestEstimateTotalGasCost(t *testing.T) {
|
|
logger := logger.New("info", "text", "")
|
|
|
|
// Test with nil parameters
|
|
totalGas := EstimateTotalGasCost(nil, nil, logger)
|
|
assert.Equal(t, big.NewInt(0), totalGas)
|
|
|
|
// Test with valid parameters
|
|
calldata := []byte("sample_transaction_data")
|
|
tx := &RPCTransaction{
|
|
Gas: "0x5208", // 21000 in decimal
|
|
}
|
|
|
|
totalGas = EstimateTotalGasCost(calldata, tx, logger)
|
|
assert.NotNil(t, totalGas)
|
|
assert.True(t, totalGas.Sign() >= 0)
|
|
|
|
// Test components are included in total
|
|
l1Fee := CalculateL1DataFee(calldata, logger)
|
|
l2Gas := CalculateL2Gas(tx, logger)
|
|
|
|
// Total should be at least the sum of components
|
|
// (in reality it might be more complex, but for our test this is sufficient)
|
|
assert.True(t, totalGas.Cmp(l1Fee) >= 0)
|
|
assert.True(t, totalGas.Cmp(l2Gas) >= 0)
|
|
}
|
|
|
|
// TestParseBigInt tests big integer parsing
|
|
func TestParseBigInt(t *testing.T) {
|
|
logger := logger.New("info", "text", "")
|
|
|
|
// Test with valid hex string
|
|
result := parseBigInt("0x1234", logger)
|
|
assert.Equal(t, big.NewInt(0x1234), result)
|
|
|
|
// Test with valid decimal string
|
|
result = parseBigInt("1234", logger)
|
|
assert.Equal(t, big.NewInt(1234), result)
|
|
|
|
// Test with zero
|
|
result = parseBigInt("0x0", logger)
|
|
assert.Equal(t, big.NewInt(0), result)
|
|
|
|
// Test with empty string
|
|
result = parseBigInt("", logger)
|
|
assert.Equal(t, big.NewInt(0), result)
|
|
|
|
// Test with invalid hex string
|
|
result = parseBigInt("0xinvalid", logger)
|
|
assert.Equal(t, big.NewInt(0), result)
|
|
|
|
// Test with invalid decimal string
|
|
result = parseBigInt("invalid", logger)
|
|
assert.Equal(t, big.NewInt(0), result)
|
|
|
|
// Test with negative number
|
|
result = parseBigInt("-1234", logger)
|
|
assert.Equal(t, big.NewInt(-1234), result)
|
|
}
|
|
|
|
// TestRPCTransaction_GetSender tests sender extraction from RPC transaction
|
|
func TestRPCTransaction_GetSender(t *testing.T) {
|
|
// Test with transaction that has from field
|
|
tx := &RPCTransaction{
|
|
From: "0x1234567890123456789012345678901234567890",
|
|
}
|
|
sender := tx.GetSender()
|
|
assert.Equal(t, common.HexToAddress("0x1234567890123456789012345678901234567890"), sender)
|
|
|
|
// Test with transaction without from field (should return zero address)
|
|
txNoFrom := &RPCTransaction{}
|
|
sender = txNoFrom.GetSender()
|
|
assert.Equal(t, common.Address{}, sender)
|
|
|
|
// Test with invalid address format
|
|
txInvalid := &RPCTransaction{
|
|
From: "invalid_address",
|
|
}
|
|
sender = txInvalid.GetSender()
|
|
assert.Equal(t, common.Address{}, sender)
|
|
}
|
|
|
|
// TestRPCTransaction_GetTo tests to address extraction from RPC transaction
|
|
func TestRPCTransaction_GetTo(t *testing.T) {
|
|
// Test with transaction that has to field
|
|
tx := &RPCTransaction{
|
|
To: "0x1234567890123456789012345678901234567890",
|
|
}
|
|
to := tx.GetTo()
|
|
assert.Equal(t, common.HexToAddress("0x1234567890123456789012345678901234567890"), to)
|
|
|
|
// Test with transaction without to field (should return zero address)
|
|
txNoTo := &RPCTransaction{}
|
|
to = txNoTo.GetTo()
|
|
assert.Equal(t, common.Address{}, to)
|
|
|
|
// Test with invalid address format
|
|
txInvalid := &RPCTransaction{
|
|
To: "invalid_address",
|
|
}
|
|
to = txInvalid.GetTo()
|
|
assert.Equal(t, common.Address{}, to)
|
|
|
|
// Test with contract creation transaction (to field is null/empty)
|
|
txContractCreation := &RPCTransaction{
|
|
To: "", // Contract creation
|
|
}
|
|
to = txContractCreation.GetTo()
|
|
assert.Equal(t, common.Address{}, to)
|
|
}
|
|
|
|
// TestRPCTransaction_GetValue tests value extraction from RPC transaction
|
|
func TestRPCTransaction_GetValue(t *testing.T) {
|
|
logger := logger.New("info", "text", "")
|
|
|
|
// Test with transaction that has value
|
|
tx := &RPCTransaction{
|
|
Value: "0x1234",
|
|
}
|
|
value := tx.GetValue(logger)
|
|
assert.Equal(t, big.NewInt(0x1234), value)
|
|
|
|
// Test with transaction without value
|
|
txNoValue := &RPCTransaction{}
|
|
value = txNoValue.GetValue(logger)
|
|
assert.Equal(t, big.NewInt(0), value)
|
|
|
|
// Test with invalid value
|
|
txInvalid := &RPCTransaction{
|
|
Value: "invalid_value",
|
|
}
|
|
value = txInvalid.GetValue(logger)
|
|
assert.Equal(t, big.NewInt(0), value)
|
|
|
|
// Test with zero value
|
|
txZero := &RPCTransaction{
|
|
Value: "0x0",
|
|
}
|
|
value = txZero.GetValue(logger)
|
|
assert.Equal(t, big.NewInt(0), value)
|
|
}
|
|
|
|
// TestRPCTransaction_GetGasPrice tests gas price extraction from RPC transaction
|
|
func TestRPCTransaction_GetGasPrice(t *testing.T) {
|
|
logger := logger.New("info", "text", "")
|
|
|
|
// Test with transaction that has gasPrice
|
|
tx := &RPCTransaction{
|
|
GasPrice: "0x1234",
|
|
}
|
|
gasPrice := tx.GetGasPrice(logger)
|
|
assert.Equal(t, big.NewInt(0x1234), gasPrice)
|
|
|
|
// Test with transaction that has maxFeePerGas (EIP-1559)
|
|
txEIP1559 := &RPCTransaction{
|
|
MaxFeePerGas: "0x1234",
|
|
}
|
|
gasPrice = txEIP1559.GetGasPrice(logger)
|
|
assert.Equal(t, big.NewInt(0x1234), gasPrice)
|
|
|
|
// Test with transaction that has both (should prefer gasPrice)
|
|
txBoth := &RPCTransaction{
|
|
GasPrice: "0x1234",
|
|
MaxFeePerGas: "0x5678",
|
|
}
|
|
gasPrice = txBoth.GetGasPrice(logger)
|
|
assert.Equal(t, big.NewInt(0x1234), gasPrice)
|
|
|
|
// Test with transaction without gas price fields
|
|
txNoGasPrice := &RPCTransaction{}
|
|
gasPrice = txNoGasPrice.GetGasPrice(logger)
|
|
assert.Equal(t, big.NewInt(0), gasPrice)
|
|
|
|
// Test with invalid gas price
|
|
txInvalid := &RPCTransaction{
|
|
GasPrice: "invalid_gas_price",
|
|
}
|
|
gasPrice = txInvalid.GetGasPrice(logger)
|
|
assert.Equal(t, big.NewInt(0), gasPrice)
|
|
}
|
|
|
|
// TestRPCTransaction_GetGasLimit tests gas limit extraction from RPC transaction
|
|
func TestRPCTransaction_GetGasLimit(t *testing.T) {
|
|
logger := logger.New("info", "text", "")
|
|
|
|
// Test with transaction that has gas limit
|
|
tx := &RPCTransaction{
|
|
Gas: "0x5208", // 21000 in decimal
|
|
}
|
|
gasLimit := tx.GetGasLimit(logger)
|
|
assert.Equal(t, big.NewInt(21000), gasLimit)
|
|
|
|
// Test with transaction without gas limit
|
|
txNoGas := &RPCTransaction{}
|
|
gasLimit = txNoGas.GetGasLimit(logger)
|
|
assert.Equal(t, big.NewInt(0), gasLimit)
|
|
|
|
// Test with invalid gas limit
|
|
txInvalid := &RPCTransaction{
|
|
Gas: "invalid_gas_limit",
|
|
}
|
|
gasLimit = txInvalid.GetGasLimit(logger)
|
|
assert.Equal(t, big.NewInt(0), gasLimit)
|
|
|
|
// Test with zero gas limit
|
|
txZero := &RPCTransaction{
|
|
Gas: "0x0",
|
|
}
|
|
gasLimit = txZero.GetGasLimit(logger)
|
|
assert.Equal(t, big.NewInt(0), gasLimit)
|
|
}
|
|
|
|
// TestRPCTransaction_GetNonce tests nonce extraction from RPC transaction
|
|
func TestRPCTransaction_GetNonce(t *testing.T) {
|
|
logger := logger.New("info", "text", "")
|
|
|
|
// Test with transaction that has nonce
|
|
tx := &RPCTransaction{
|
|
Nonce: "0x1234",
|
|
}
|
|
nonce := tx.GetNonce(logger)
|
|
assert.Equal(t, uint64(0x1234), nonce)
|
|
|
|
// Test with transaction without nonce
|
|
txNoNonce := &RPCTransaction{}
|
|
nonce = txNoNonce.GetNonce(logger)
|
|
assert.Equal(t, uint64(0), nonce)
|
|
|
|
// Test with invalid nonce
|
|
txInvalid := &RPCTransaction{
|
|
Nonce: "invalid_nonce",
|
|
}
|
|
nonce = txInvalid.GetNonce(logger)
|
|
assert.Equal(t, uint64(0), nonce)
|
|
|
|
// Test with zero nonce
|
|
txZero := &RPCTransaction{
|
|
Nonce: "0x0",
|
|
}
|
|
nonce = txZero.GetNonce(logger)
|
|
assert.Equal(t, uint64(0), nonce)
|
|
}
|
|
|
|
// TestRPCTransaction_GetInput tests input data extraction from RPC transaction
|
|
func TestRPCTransaction_GetInput(t *testing.T) {
|
|
// Test with transaction that has input data
|
|
inputData := "0x1234567890abcdef"
|
|
tx := &RPCTransaction{
|
|
Input: inputData,
|
|
}
|
|
input := tx.GetInput()
|
|
assert.Equal(t, inputData, input)
|
|
|
|
// Test with transaction without input data
|
|
txNoInput := &RPCTransaction{}
|
|
input = txNoInput.GetInput()
|
|
assert.Equal(t, "", input)
|
|
|
|
// Test with empty input data
|
|
txEmpty := &RPCTransaction{
|
|
Input: "",
|
|
}
|
|
input = txEmpty.GetInput()
|
|
assert.Equal(t, "", input)
|
|
}
|
|
|
|
// TestRPCTransaction_IsContractCreation tests contract creation detection
|
|
func TestRPCTransaction_IsContractCreation(t *testing.T) {
|
|
// Test contract creation transaction (to field is null/empty)
|
|
txContractCreation := &RPCTransaction{
|
|
To: "", // Empty to field indicates contract creation
|
|
}
|
|
isCreation := txContractCreation.IsContractCreation()
|
|
assert.True(t, isCreation)
|
|
|
|
// Test regular transaction (to field is present)
|
|
txRegular := &RPCTransaction{
|
|
To: "0x1234567890123456789012345678901234567890",
|
|
}
|
|
isCreation = txRegular.IsContractCreation()
|
|
assert.False(t, isCreation)
|
|
|
|
// Test with nil to field
|
|
txNilTo := &RPCTransaction{
|
|
To: "", // Nil becomes empty string in JSON unmarshaling
|
|
}
|
|
isCreation = txNilTo.IsContractCreation()
|
|
assert.True(t, isCreation)
|
|
}
|
|
|
|
// TestCalculateL1DataFeeComponents tests the components of L1 data fee calculation
|
|
func TestCalculateL1DataFeeComponents(t *testing.T) {
|
|
logger := logger.New("info", "text", "")
|
|
|
|
// Test calldata cost calculation
|
|
calldata := []byte{0x00, 0x01, 0x02, 0x03, 0x04}
|
|
calldataCost := calculateCalldataCost(calldata)
|
|
assert.True(t, calldataCost >= int64(len(calldata)))
|
|
|
|
// Test with zero bytes (should cost less)
|
|
zeroBytes := []byte{0x00, 0x00, 0x00}
|
|
zeroCost := calculateCalldataCost(zeroBytes)
|
|
assert.True(t, zeroCost >= 0)
|
|
|
|
// Test with non-zero bytes (should cost more)
|
|
nonZeroBytes := []byte{0x01, 0x02, 0x03}
|
|
nonZeroCost := calculateCalldataCost(nonZeroBytes)
|
|
assert.True(t, nonZeroCost >= int64(len(nonZeroBytes)))
|
|
|
|
// Test L1 gas price parsing
|
|
validL1GasPrice := "0x3b9aca00" // 1 Gwei
|
|
l1GasPrice := parseL1GasPrice(validL1GasPrice, logger)
|
|
assert.Equal(t, big.NewInt(1000000000), l1GasPrice)
|
|
|
|
// Test invalid L1 gas price
|
|
invalidL1GasPrice := "invalid"
|
|
l1GasPrice = parseL1GasPrice(invalidL1GasPrice, logger)
|
|
assert.Equal(t, big.NewInt(defaultL1GasPrice), l1GasPrice)
|
|
|
|
// Test L1 fee calculation with parameters
|
|
calldataCostInt := int64(100)
|
|
l1GasPriceBig := big.NewInt(1000000000) // 1 Gwei
|
|
l1Fee := calculateL1Fee(calldataCostInt, l1GasPriceBig, logger)
|
|
assert.NotNil(t, l1Fee)
|
|
assert.True(t, l1Fee.Sign() >= 0)
|
|
}
|
|
|
|
// Helper function to calculate calldata cost (simulating internal logic)
|
|
func calculateCalldataCost(data []byte) int64 {
|
|
cost := int64(0)
|
|
for _, b := range data {
|
|
if b == 0 {
|
|
cost += 4 // Cost for zero byte
|
|
} else {
|
|
cost += 16 // Cost for non-zero byte
|
|
}
|
|
}
|
|
return cost
|
|
}
|
|
|
|
// Helper function to parse L1 gas price
|
|
func parseL1GasPrice(gasPrice string, logger *logger.Logger) *big.Int {
|
|
if gasPrice == "" {
|
|
return big.NewInt(defaultL1GasPrice)
|
|
}
|
|
|
|
result, ok := new(big.Int).SetString(gasPrice, 0)
|
|
if !ok {
|
|
logger.Warn("Invalid L1 gas price, using default")
|
|
return big.NewInt(defaultL1GasPrice)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Helper function to calculate L1 fee
|
|
func calculateL1Fee(calldataCost int64, l1GasPrice *big.Int, logger *logger.Logger) *big.Int {
|
|
// Simplified L1 fee calculation
|
|
// In reality, this would involve more complex Arbitrum-specific calculations
|
|
baseFee := big.NewInt(calldataCost)
|
|
fee := new(big.Int).Mul(baseFee, l1GasPrice)
|
|
|
|
// Apply scaling factor (simplified)
|
|
scalingFactor := big.NewInt(1000000000) // 1e9
|
|
fee.Div(fee, scalingFactor)
|
|
|
|
return fee
|
|
}
|
|
|
|
// TestGasConstants tests gas-related constants
|
|
func TestGasConstants(t *testing.T) {
|
|
// Test that default L1 gas price is reasonable (1 Gwei = 1,000,000,000 wei)
|
|
assert.Equal(t, int64(1000000000), defaultL1GasPrice)
|
|
|
|
// Test that default L2 gas limit is reasonable
|
|
assert.Equal(t, int64(30000000), defaultL2GasLimit)
|
|
|
|
// Test that zero byte cost is less than non-zero byte cost
|
|
assert.True(t, l1ZeroByteCost < l1NonZeroByteCost)
|
|
}
|