Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions devnet-sdk/constraints/constraints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ func (m mockWallet) SendETH(to types.Address, amount types.Balance) types.WriteI
panic("not implemented")
}

func (m mockWallet) InitiateMessage(chainID types.ChainID, target common.Address, message []byte) types.WriteInvocation[any] {
panic("not implemented")
}

func (m mockWallet) Nonce() uint64 {
return 0
}
Expand Down
788 changes: 788 additions & 0 deletions devnet-sdk/contracts/bindings/l2tol2crossdomainmessenger.go

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions devnet-sdk/contracts/registry/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package client

import (
"fmt"
"strings"

"github.com/ethereum-optimism/optimism/devnet-sdk/contracts/bindings"
"github.com/ethereum-optimism/optimism/devnet-sdk/interfaces"
"github.com/ethereum-optimism/optimism/devnet-sdk/types"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/ethclient"
)

Expand All @@ -27,3 +29,20 @@ func (r *ClientRegistry) SuperchainWETH(address types.Address) (interfaces.Super
binding: binding,
}, nil
}

func (r *ClientRegistry) L2ToL2CrossDomainMessenger(address types.Address) (interfaces.L2ToL2CrossDomainMessenger, error) {
binding, err := bindings.NewL2ToL2CrossDomainMessenger(address, r.Client)
if err != nil {
return nil, fmt.Errorf("failed to create L2ToL2CrossDomainMessenger binding: %w", err)
}
abi, err := abi.JSON(strings.NewReader(bindings.L2ToL2CrossDomainMessengerMetaData.ABI))
if err != nil {
return nil, fmt.Errorf("failed to create L2ToL2CrossDomainMessenger binding ABI: %w", err)
}
return &L2ToL2CrossDomainMessengerBinding{
contractAddress: address,
client: r.Client,
binding: binding,
abi: &abi,
}, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package client

import (
"github.com/ethereum-optimism/optimism/devnet-sdk/contracts/bindings"
"github.com/ethereum-optimism/optimism/devnet-sdk/interfaces"
"github.com/ethereum-optimism/optimism/devnet-sdk/types"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/ethclient"
)

type L2ToL2CrossDomainMessengerBinding struct {
contractAddress types.Address
client *ethclient.Client
binding *bindings.L2ToL2CrossDomainMessenger
abi *abi.ABI
}

var _ interfaces.L2ToL2CrossDomainMessenger = (*L2ToL2CrossDomainMessengerBinding)(nil)

func (b *L2ToL2CrossDomainMessengerBinding) ABI() *abi.ABI {
return b.abi
}
7 changes: 7 additions & 0 deletions devnet-sdk/contracts/registry/empty/empty.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,10 @@ func (r *EmptyRegistry) SuperchainWETH(address types.Address) (interfaces.Superc
Address: address,
}
}

func (r *EmptyRegistry) L2ToL2CrossDomainMessenger(address types.Address) (interfaces.L2ToL2CrossDomainMessenger, error) {
return nil, &interfaces.ErrContractNotFound{
ContractType: "L2ToL2CrossDomainMessenger",
Address: address,
}
}
6 changes: 6 additions & 0 deletions devnet-sdk/interfaces/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

"github.com/ethereum-optimism/optimism/devnet-sdk/types"
"github.com/ethereum/go-ethereum/accounts/abi"
)

// ErrContractNotFound indicates that a contract is not available at the requested address
Expand All @@ -19,9 +20,14 @@ func (e *ErrContractNotFound) Error() string {
// ContractsRegistry provides access to all supported contract instances
type ContractsRegistry interface {
SuperchainWETH(address types.Address) (SuperchainWETH, error)
L2ToL2CrossDomainMessenger(address types.Address) (L2ToL2CrossDomainMessenger, error)
}

// SuperchainWETH represents the interface for interacting with the SuperchainWETH contract
type SuperchainWETH interface {
BalanceOf(user types.Address) types.ReadInvocation[types.Balance]
}

type L2ToL2CrossDomainMessenger interface {
ABI() *abi.ABI
}
1 change: 1 addition & 0 deletions devnet-sdk/system/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type Wallet interface {
PrivateKey() types.Key
Address() types.Address
SendETH(to types.Address, amount types.Balance) types.WriteInvocation[any]
InitiateMessage(chainID types.ChainID, target common.Address, message []byte) types.WriteInvocation[any]
Balance() types.Balance
Nonce() uint64

Expand Down
5 changes: 5 additions & 0 deletions devnet-sdk/system/txbuilder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ func (m *mockWallet) SendETH(to types.Address, amount types.Balance) types.Write
return args.Get(0).(types.WriteInvocation[any])
}

func (m *mockWallet) InitiateMessage(chainID types.ChainID, target common.Address, message []byte) types.WriteInvocation[any] {
args := m.Called(chainID, target, message)
return args.Get(0).(types.WriteInvocation[any])
}

func (m *mockWallet) Balance() types.Balance {
args := m.Called()
return args.Get(0).(types.Balance)
Expand Down
69 changes: 69 additions & 0 deletions devnet-sdk/system/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import (
"context"
"encoding/hex"
"fmt"
"math/big"
"strings"

"github.com/ethereum-optimism/optimism/devnet-sdk/contracts/constants"
"github.com/ethereum-optimism/optimism/devnet-sdk/types"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"

Expand Down Expand Up @@ -100,6 +103,72 @@ func (w *wallet) Balance() types.Balance {
return types.NewBalance(balance)
}

func (w *wallet) InitiateMessage(chainID types.ChainID, target common.Address, message []byte) types.WriteInvocation[any] {
return &initiateMessageImpl{
chain: w.chain,
processor: w,
from: w.address,
target: target,
chainID: chainID,
message: message,
}
}

type initiateMessageImpl struct {
chain internalChain
processor TransactionProcessor
from types.Address

target types.Address
chainID types.ChainID
message []byte
}

func (i *initiateMessageImpl) Call(ctx context.Context) (any, error) {
builder := NewTxBuilder(ctx, i.chain)
messenger, err := i.chain.ContractsRegistry().L2ToL2CrossDomainMessenger(constants.L2ToL2CrossDomainMessenger)
if err != nil {
return nil, fmt.Errorf("failed to init transaction: %w", err)
}
data, err := messenger.ABI().Pack("sendMessage", i.chainID, i.target, i.message)
if err != nil {
return nil, fmt.Errorf("failed to build calldata: %w", err)
}
tx, err := builder.BuildTx(
WithFrom(i.from),
WithTo(constants.L2ToL2CrossDomainMessenger),
WithValue(big.NewInt(0)),
WithData(data),
)
if err != nil {
return nil, fmt.Errorf("failed to build transaction: %w", err)
}

tx, err = i.processor.Sign(tx)
if err != nil {
return nil, fmt.Errorf("failed to sign transaction: %w", err)
}

return tx, nil
}

func (i *initiateMessageImpl) Send(ctx context.Context) types.InvocationResult {
result, err := i.Call(ctx)
if err != nil {
return &sendResult{chain: i.chain, tx: nil, err: err}
}
tx, ok := result.(Transaction)
if !ok {
return &sendResult{chain: i.chain, tx: nil, err: fmt.Errorf("unexpected return type")}
}
err = i.processor.Send(ctx, tx)
return &sendResult{
chain: i.chain,
tx: tx,
err: err,
}
}

func (w *wallet) Nonce() uint64 {
client, err := w.chain.Client()
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions devnet-sdk/testing/testlib/validators/validators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ func (m mockWallet) SendETH(to types.Address, amount types.Balance) types.WriteI
panic("not implemented")
}

func (m mockWallet) InitiateMessage(chainID types.ChainID, target common.Address, message []byte) types.WriteInvocation[any] {
panic("not implemented")
}

func (m mockWallet) Nonce() uint64 {
return 0
}
Expand Down
50 changes: 50 additions & 0 deletions op-acceptance-tests/tests/interop/interop_tx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package interop

import (
"math/big"
"testing"

"github.com/ethereum-optimism/optimism/devnet-sdk/contracts/constants"
"github.com/ethereum-optimism/optimism/devnet-sdk/system"
"github.com/ethereum-optimism/optimism/devnet-sdk/testing/systest"
"github.com/ethereum-optimism/optimism/devnet-sdk/testing/testlib/validators"
sdktypes "github.com/ethereum-optimism/optimism/devnet-sdk/types"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)

func initiateMessageScenario(sourceChainIdx, destChainIdx uint64, walletGetter validators.WalletGetter) systest.InteropSystemTestFunc {
return func(t systest.T, sys system.InteropSystem) {
ctx := t.Context()

logger := testlog.Logger(t, log.LevelInfo)
logger = logger.With("test", "TestInitiateMessage", "devnet", sys.Identifier())

chainA := sys.L2s()[sourceChainIdx]
chainB := sys.L2s()[destChainIdx]

logger = logger.With("sourceChain", chainA.ID(), "destChain", chainB.ID())

// userA is funded at chainA and want to send message to chainB
userA := walletGetter(ctx)

// Initiate message
dummyAddress := common.Address{0x13, 0x37}
dummyMessage := []byte{0x13, 0x33, 0x33, 0x37}
logger.Info("Initiate message", "address", dummyAddress, "message", dummyMessage)
require.NoError(t, userA.InitiateMessage(chainB.ID(), dummyAddress, dummyMessage).Send(ctx).Wait())
}
}

func TestInteropSystemInitiateMessage(t *testing.T) {
sourceChainIdx := uint64(0)
destChainIdx := uint64(1)
walletGetter, fundsValidator := validators.AcquireL2WalletWithFunds(sourceChainIdx, sdktypes.NewBalance(big.NewInt(1.0*constants.ETH)))

systest.InteropSystemTest(t,
initiateMessageScenario(sourceChainIdx, destChainIdx, walletGetter),
fundsValidator,
)
}
4 changes: 4 additions & 0 deletions op-acceptance-tests/tests/interop/mocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ func (m *mockFailingWallet) SendETH(to types.Address, amount types.Balance) type
return &mockFailingTx{}
}

func (m *mockFailingWallet) InitiateMessage(chainID types.ChainID, target common.Address, message []byte) types.WriteInvocation[any] {
return &mockFailingTx{}
}

func (m *mockFailingWallet) Nonce() uint64 {
return 0
}
Expand Down