Skip to content

Commit

Permalink
chore(solver/app): approve outbox spend in fulfil (#2572)
Browse files Browse the repository at this point in the history
Before submitting fulfil to outbox, approve it to spend token prereqs.

issue: #2571

---------

Co-authored-by: Kevin Halliday <[email protected]>
  • Loading branch information
2 people authored and chmllr committed Dec 3, 2024
1 parent 573a6cb commit 017221d
Show file tree
Hide file tree
Showing 16 changed files with 454 additions and 80 deletions.
1 change: 1 addition & 0 deletions contracts/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ install-deps: check-pnpm-version ## Install dependencies.
build: version ## Build contracts.
forge build --force --root core
forge build --force --root avs
forge build --force --root solve


.PHONY: all
Expand Down
8 changes: 8 additions & 0 deletions e2e/app/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/omni-network/omni/e2e/netman"
"github.com/omni-network/omni/e2e/netman/pingpong"
"github.com/omni-network/omni/e2e/solve"
"github.com/omni-network/omni/e2e/solve/devapp"
"github.com/omni-network/omni/e2e/types"
"github.com/omni-network/omni/halo/genutil/evm/predeploys"
"github.com/omni-network/omni/lib/contracts"
Expand Down Expand Up @@ -171,6 +172,13 @@ func E2ETest(ctx context.Context, def Definition, cfg E2ETestConfig) error {
return err
}

if def.Manifest.DeploySolve {
// TODO(corver): Remove this
if err := devapp.TestFlow(ctx, NetworkFromDef(def), ExternalEndpoints(def)); err != nil {
return err
}
}

var eg errgroup.Group
eg.Go(func() error { return testGasPumps(ctx, def) })
eg.Go(func() error { return testBridge(ctx, def) })
Expand Down
6 changes: 5 additions & 1 deletion e2e/solve/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ func DeployContracts(ctx context.Context, network netconf.Network, backends ethb
var eg errgroup.Group

eg.Go(func() error {
return deployBoxes(ctx, network, backends)
if err := deployBoxes(ctx, network, backends); err != nil {
return errors.Wrap(err, "deploy boxes")
}

return devapp.AllowOutboxCalls(ctx, network, backends)
})
eg.Go(func() error {
return devapp.Deploy(ctx, network, backends)
Expand Down
112 changes: 94 additions & 18 deletions e2e/solve/devapp/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/omni-network/omni/contracts/bindings"
"github.com/omni-network/omni/e2e/app/eoa"
"github.com/omni-network/omni/lib/cast"
"github.com/omni-network/omni/lib/contracts"
"github.com/omni-network/omni/lib/create3"
"github.com/omni-network/omni/lib/errors"
Expand All @@ -13,6 +14,9 @@ import (
"github.com/omni-network/omni/lib/netconf"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"

"cosmossdk.io/math"
)

const (
Expand All @@ -21,47 +25,119 @@ const (
l2TokenSalt = "l2-token"
)

var (
create3Factory = contracts.Create3Factory(netconf.Devnet)
deployer = eoa.MustAddress(netconf.Devnet, eoa.RoleDeployer)
)

// Deploy deploys the mock tokens and vaults to devnet.
func Deploy(ctx context.Context, network netconf.Network, backends ethbackend.Backends) error {
if network.ID != netconf.Devnet {
return errors.New("onl devnet")
}

mockl1, ok := network.Chain(evmchain.IDMockL1)
if !ok {
return errors.New("no mock l1")
l1Backend, err := backends.Backend(static.L1.ChainID)
if err != nil {
return errors.Wrap(err, "backend mock l1")
}

l2Backend, err := backends.Backend(static.L2.ChainID)
if err != nil {
return errors.Wrap(err, "backend mock l2")
}

if err := deployToken(ctx, l1Backend, l1TokenSalt); err != nil {
return errors.Wrap(err, "deploy l1 token")
}

if err := deployVault(ctx, l1Backend, l1VaultSalt, static.L1Token); err != nil {
return errors.Wrap(err, "deploy vault")
}

if err := deployToken(ctx, l2Backend, l2TokenSalt); err != nil {
return errors.Wrap(err, "deploy l2 token")
}

if err := fundSolver(ctx, l1Backend, static.L1Token); err != nil {
return errors.Wrap(err, "fund solver")
}

return nil
}

func fundSolver(ctx context.Context, backend *ethbackend.Backend, tokenAddr common.Address) error {
mngr := eoa.MustAddress(netconf.Devnet, eoa.RoleManager) // we use mngr to mint, but this doesn't matter. could be any dev addr
slvr := eoa.MustAddress(netconf.Devnet, eoa.RoleSolver)

token, err := bindings.NewMockToken(tokenAddr, backend)
if err != nil {
return errors.Wrap(err, "new mock token")
}

txOpts, err := backend.BindOpts(ctx, mngr)
if err != nil {
return errors.Wrap(err, "bind opts")
}

mockl2, ok := network.Chain(evmchain.IDMockL2)
eth1m := math.NewInt(1_000_000).MulRaw(params.Ether).BigInt()
tx, err := token.Mint(txOpts, slvr, eth1m)
if err != nil {
return errors.Wrap(err, "mint")
}

_, err = backend.WaitMined(ctx, tx)
if err != nil {
return errors.Wrap(err, "wait mined")
}

return nil
}

// AllowOutboxCalls allows the outbox to call the L1 vault deposit method.
func AllowOutboxCalls(ctx context.Context, network netconf.Network, backends ethbackend.Backends) error {
if network.ID != netconf.Devnet {
return errors.New("onl devnet")
}

addrs, err := contracts.GetAddresses(ctx, network.ID)
if err != nil {
return errors.Wrap(err, "get addresses")
}

mockl1, ok := network.Chain(evmchain.IDMockL1)
if !ok {
return errors.New("no mock l2")
return errors.New("no mock l1")
}

mockl1Backend, err := backends.Backend(mockl1.ID)
if err != nil {
return errors.Wrap(err, "backend mock l1")
}

mockl2Backend, err := backends.Backend(mockl2.ID)
if err := allowCalls(ctx, mockl1Backend, addrs.SolveOutbox); err != nil {
return errors.Wrap(err, "allow calls")
}

return nil
}

// allowCalls allows the outbox to call the L1 vault deposit method.
func allowCalls(ctx context.Context, backend *ethbackend.Backend, outboxAddr common.Address) error {
outbox, err := bindings.NewSolveOutbox(outboxAddr, backend)
if err != nil {
return errors.Wrap(err, "backend mock l2")
return errors.Wrap(err, "new solve outbox")
}

if err := deployToken(ctx, mockl1Backend, l1TokenSalt); err != nil {
return errors.Wrap(err, "deploy l1 token")
txOpts, err := backend.BindOpts(ctx, manager)
if err != nil {
return errors.Wrap(err, "bind opts")
}

if err := deployVault(ctx, mockl1Backend, l1VaultSalt, static.L1Token); err != nil {
return errors.Wrap(err, "deploy vault")
vaultDepositID, err := cast.Array4(vaultDeposit.ID[:4])
if err != nil {
return err
}

if err := deployToken(ctx, mockl2Backend, l2TokenSalt); err != nil {
return errors.Wrap(err, "deploy l2 token")
tx, err := outbox.SetAllowedCall(txOpts, static.L1Vault, vaultDepositID, true)
if err != nil {
return errors.Wrap(err, "set allowed call")
} else if _, err := backend.WaitMined(ctx, tx); err != nil {
return errors.Wrap(err, "wait mined")
}

return nil
Expand Down
120 changes: 116 additions & 4 deletions e2e/solve/devapp/deposit.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package devapp
import (
"context"
"math/big"
"time"

"github.com/omni-network/omni/contracts/bindings"
"github.com/omni-network/omni/lib/anvil"
"github.com/omni-network/omni/lib/contracts"
"github.com/omni-network/omni/lib/errors"
"github.com/omni-network/omni/lib/ethclient/ethbackend"
"github.com/omni-network/omni/lib/log"
"github.com/omni-network/omni/lib/netconf"
"github.com/omni-network/omni/lib/xchain"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -78,7 +81,107 @@ func IsDeposited(ctx context.Context, backends ethbackend.Backends, req DepositR

// assumes balance(onBehalfOf) was zero before deposit request
// assumes one deposit per test case onBehalfOf addr
return balance.Cmp(req.Deposit.Amount) != 0, nil
return balance.Cmp(req.Deposit.Amount) == 0, nil
}

// TestFlow submits deposit requests to the solve inbox and waits for them to be processed.
func TestFlow(ctx context.Context, network netconf.Network, endpoints xchain.RPCEndpoints) error {
backends, err := ethbackend.BackendsFromNetwork(network, endpoints)
if err != nil {
return err
}

deposits, err := RequestDeposits(ctx, backends)
if err != nil {
return err
}

timeout, cancel := context.WithTimeout(ctx, time.Minute)
defer cancel()

// Wait for all deposits to be completed on dest chain by solver/outbox
toCheck := toSet(deposits)
for {
if timeout.Err() != nil {
return errors.New("timeout waiting for deposits")
}

for deposit := range toCheck {
ok, err := IsDeposited(ctx, backends, deposit)
if err != nil {
return err
} else if ok {
log.Debug(ctx, "Deposit complete", "remaining", len(toCheck)-1)
delete(toCheck, deposit)
}
}

if len(toCheck) == 0 {
break
}

time.Sleep(time.Second)
}

log.Debug(ctx, "All deposits fulfilled")

// Wait for requests to be claimed by solver
toCheck = toSet(deposits)
for {
if timeout.Err() != nil {
return errors.New("timeout waiting for claims")
}

const statusClaimed = 6

for deposit := range toCheck {
status, err := GetDepositStatus(ctx, backends, deposit)
if err != nil {
return err
} else if status == statusClaimed {
log.Debug(ctx, "Deposit claimed", "remaining", len(toCheck)-1)
delete(toCheck, deposit)
}
}

if len(toCheck) == 0 {
break
}

time.Sleep(time.Second)
}

log.Debug(ctx, "All deposits claimed")

return nil
}

func GetDepositStatus(ctx context.Context, backends ethbackend.Backends, deposit DepositReq) (uint8, error) {
app := GetApp()

backend, err := backends.Backend(app.L2.ChainID)
if err != nil {
return 0, errors.Wrap(err, "backend")
}

addrs, err := contracts.GetAddresses(ctx, netconf.Devnet)
if err != nil {
return 0, errors.Wrap(err, "get addresses")
}

inbox, err := bindings.NewSolveInbox(addrs.SolveInbox, backend)
if err != nil {
return 0, errors.Wrap(err, "new mock vault")
}

callOpts := &bind.CallOpts{Context: ctx}

req, err := inbox.GetRequest(callOpts, deposit.ID)
if err != nil {
return 0, errors.Wrap(err, "get balance")
}

return req.Status, nil
}

// addRandomDepositors adds n random depositors privkeys to the backend.
Expand Down Expand Up @@ -224,10 +327,19 @@ func parseReqID(inbox bindings.SolveInboxFilterer, logs []*types.Log) ([32]byte,
}

func packDeposit(args DepositArgs) ([]byte, error) {
data, err := vaultDeposit.Inputs.Pack(args.OnBehalfOf, args.Amount)
calldata, err := vaultABI.Pack("deposit", args.OnBehalfOf, args.Amount)
if err != nil {
return nil, errors.Wrap(err, "pack data")
return nil, errors.Wrap(err, "pack deposit call data")
}

return calldata, nil
}

func toSet[T comparable](slice []T) map[T]bool {
set := make(map[T]bool)
for _, v := range slice {
set[v] = true
}

return data, nil
return set
}
27 changes: 27 additions & 0 deletions e2e/solve/devapp/deposit_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package devapp

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"

"github.com/stretchr/testify/require"
)

func TestPackUnpack(t *testing.T) {
t.Parallel()

dep := DepositArgs{
OnBehalfOf: common.Address{},
Amount: big.NewInt(1),
}

packed, err := packDeposit(dep)
require.NoError(t, err)

dep2, err := unpackDeposit(packed)
require.NoError(t, err)

require.Equal(t, dep, dep2)
}
Loading

0 comments on commit 017221d

Please sign in to comment.