batch_test.go 3.94 KB
Newer Older
1 2 3 4 5
package safe

import (
	"bytes"
	"encoding/json"
6
	"errors"
Mark Tyneway's avatar
Mark Tyneway committed
7
	"math/big"
8 9 10
	"os"
	"testing"

Mark Tyneway's avatar
Mark Tyneway committed
11 12
	"github.com/ethereum-optimism/optimism/op-bindings/bindings"

Mark Tyneway's avatar
Mark Tyneway committed
13 14 15
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"

16 17 18
	"github.com/stretchr/testify/require"
)

Mark Tyneway's avatar
Mark Tyneway committed
19 20
func TestBatchJSONPrepareBedrock(t *testing.T) {
	testBatchJSON(t, "testdata/batch-prepare-bedrock.json")
21 22
}

Mark Tyneway's avatar
Mark Tyneway committed
23 24
func TestBatchJSONL2OO(t *testing.T) {
	testBatchJSON(t, "testdata/l2-output-oracle.json")
25 26
}

Mark Tyneway's avatar
Mark Tyneway committed
27
func testBatchJSON(t *testing.T, path string) {
28 29 30
	b, err := os.ReadFile(path)
	require.NoError(t, err)
	dec := json.NewDecoder(bytes.NewReader(b))
Mark Tyneway's avatar
Mark Tyneway committed
31
	decoded := new(Batch)
32 33 34 35 36
	require.NoError(t, dec.Decode(decoded))
	data, err := json.Marshal(decoded)
	require.NoError(t, err)
	require.JSONEq(t, string(b), string(data))
}
Mark Tyneway's avatar
Mark Tyneway committed
37

Mark Tyneway's avatar
Mark Tyneway committed
38
// TestBatchAddCallFinalizeWithdrawalTransaction ensures that structs can be serialized correctly.
Mark Tyneway's avatar
Mark Tyneway committed
39
func TestBatchAddCallFinalizeWithdrawalTransaction(t *testing.T) {
Mark Tyneway's avatar
Mark Tyneway committed
40 41 42 43 44
	file, err := os.ReadFile("testdata/portal-abi.json")
	require.NoError(t, err)
	portalABI, err := abi.JSON(bytes.NewReader(file))
	require.NoError(t, err)

Mark Tyneway's avatar
Mark Tyneway committed
45 46 47 48 49 50 51 52 53 54 55 56
	sig := "finalizeWithdrawalTransaction"
	argument := []any{
		bindings.TypesWithdrawalTransaction{
			Nonce:    big.NewInt(0),
			Sender:   common.Address{19: 0x01},
			Target:   common.Address{19: 0x02},
			Value:    big.NewInt(1),
			GasLimit: big.NewInt(2),
			Data:     []byte{},
		},
	}

Mark Tyneway's avatar
Mark Tyneway committed
57
	batch := new(Batch)
Mark Tyneway's avatar
Mark Tyneway committed
58 59
	to := common.Address{19: 0x01}
	value := big.NewInt(222)
Mark Tyneway's avatar
Mark Tyneway committed
60

Mark Tyneway's avatar
Mark Tyneway committed
61
	require.NoError(t, batch.AddCall(to, value, sig, argument, portalABI))
62 63 64
	require.NoError(t, batch.Check())
	require.Equal(t, batch.Transactions[0].Signature(), "finalizeWithdrawalTransaction((uint256,address,address,uint256,uint256,bytes))")

Mark Tyneway's avatar
Mark Tyneway committed
65
	expected, err := os.ReadFile("testdata/finalize-withdrawal-tx.json")
Mark Tyneway's avatar
Mark Tyneway committed
66
	require.NoError(t, err)
Mark Tyneway's avatar
Mark Tyneway committed
67 68 69 70

	serialized, err := json.Marshal(batch)
	require.NoError(t, err)
	require.JSONEq(t, string(expected), string(serialized))
Mark Tyneway's avatar
Mark Tyneway committed
71 72
}

Mark Tyneway's avatar
Mark Tyneway committed
73
// TestBatchAddCallDespostTransaction ensures that simple calls can be serialized correctly.
Mark Tyneway's avatar
Mark Tyneway committed
74
func TestBatchAddCallDespositTransaction(t *testing.T) {
Mark Tyneway's avatar
Mark Tyneway committed
75 76 77 78 79 80
	file, err := os.ReadFile("testdata/portal-abi.json")
	require.NoError(t, err)
	portalABI, err := abi.JSON(bytes.NewReader(file))
	require.NoError(t, err)

	batch := new(Batch)
Mark Tyneway's avatar
Mark Tyneway committed
81 82
	to := common.Address{19: 0x01}
	value := big.NewInt(222)
Mark Tyneway's avatar
Mark Tyneway committed
83 84 85 86 87 88 89
	sig := "depositTransaction"
	argument := []any{
		common.Address{01},
		big.NewInt(2),
		uint64(100),
		false,
		[]byte{},
Mark Tyneway's avatar
Mark Tyneway committed
90 91
	}

Mark Tyneway's avatar
Mark Tyneway committed
92
	require.NoError(t, batch.AddCall(to, value, sig, argument, portalABI))
93 94 95
	require.NoError(t, batch.Check())
	require.Equal(t, batch.Transactions[0].Signature(), "depositTransaction(address,uint256,uint64,bool,bytes)")

Mark Tyneway's avatar
Mark Tyneway committed
96 97 98 99
	expected, err := os.ReadFile("testdata/deposit-tx.json")
	require.NoError(t, err)

	serialized, err := json.Marshal(batch)
Mark Tyneway's avatar
Mark Tyneway committed
100
	require.NoError(t, err)
Mark Tyneway's avatar
Mark Tyneway committed
101
	require.JSONEq(t, string(expected), string(serialized))
Mark Tyneway's avatar
Mark Tyneway committed
102
}
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156

// TestBatchCheck checks for the various failure cases of Batch.Check
// as well as a simple check for a valid batch.
func TestBatchCheck(t *testing.T) {
	cases := []struct {
		name string
		bt   BatchTransaction
		err  error
	}{
		{
			name: "bad-input-count",
			bt: BatchTransaction{
				Method: ContractMethod{},
				InputValues: map[string]string{
					"foo": "bar",
				},
			},
			err: errors.New("expected 0 inputs but got 1"),
		},
		{
			name: "bad-calldata-too-small",
			bt: BatchTransaction{
				Data: []byte{0x01},
			},
			err: errors.New("must have at least 4 bytes of calldata, got 1"),
		},
		{
			name: "bad-calldata-mismatch",
			bt: BatchTransaction{
				Data: []byte{0x01, 0x02, 0x03, 0x04},
				Method: ContractMethod{
					Name: "foo",
				},
			},
			err: errors.New("data does not match signature"),
		},
		{
			name: "good-calldata",
			bt: BatchTransaction{
				Data: []byte{0xc2, 0x98, 0x55, 0x78},
				Method: ContractMethod{
					Name: "foo",
				},
			},
			err: nil,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			require.Equal(t, tc.err, tc.bt.Check())
		})
	}
}