Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(lib/contracts): move solvernet utils to lib #2854

Merged
merged 1 commit into from
Jan 22, 2025
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
83 changes: 83 additions & 0 deletions lib/contracts/solvernet/bindings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package solvernet

import (
"github.com/omni-network/omni/contracts/bindings"
"github.com/omni-network/omni/lib/errors"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
)

var (
bindingsABI = mustGetABI(bindings.ISolverNetBindingsMetaData)
inputsOrderData = mustGetInputs(bindingsABI, "orderData")
inputsFillOriginData = mustGetInputs(bindingsABI, "fillOriginData")
)

func mustGetABI(metadata *bind.MetaData) *abi.ABI {
abi, err := metadata.GetAbi()
if err != nil {
panic(err)
}

return abi
}

func mustGetInputs(abi *abi.ABI, name string) abi.Arguments {
method, ok := abi.Methods[name]
if !ok {
panic("method not found")
}

return method.Inputs
}

func ParseFillOriginData(data []byte) (bindings.ISolverNetFillOriginData, error) {
unpacked, err := inputsFillOriginData.Unpack(data)
if err != nil {
return bindings.ISolverNetFillOriginData{}, errors.Wrap(err, "unpack fill data")
}

wrap := struct {
Data bindings.ISolverNetFillOriginData
}{}
if err := inputsFillOriginData.Copy(&wrap, unpacked); err != nil {
return bindings.ISolverNetFillOriginData{}, errors.Wrap(err, "copy fill data")
}

return wrap.Data, nil
}

func ParseOrderData(data []byte) (bindings.ISolverNetOrderData, error) {
unpacked, err := inputsOrderData.Unpack(data)
if err != nil {
return bindings.ISolverNetOrderData{}, errors.Wrap(err, "unpack fill data")
}

wrap := struct {
Data bindings.ISolverNetOrderData
}{}
if err := inputsOrderData.Copy(&wrap, unpacked); err != nil {
return bindings.ISolverNetOrderData{}, errors.Wrap(err, "copy fill data")
}

return wrap.Data, nil
}

func PackOrderData(data bindings.ISolverNetOrderData) ([]byte, error) {
packed, err := inputsOrderData.Pack(data)
if err != nil {
return nil, errors.Wrap(err, "pack fill data")
}

return packed, nil
}

func PackFillOriginData(data bindings.ISolverNetFillOriginData) ([]byte, error) {
packed, err := inputsFillOriginData.Pack(data)
if err != nil {
return nil, errors.Wrap(err, "pack fill data")
}

return packed, nil
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package appv2
package solvernet_test

import (
"math/big"
"testing"

"github.com/omni-network/omni/contracts/bindings"
"github.com/omni-network/omni/lib/contracts/solvernet"

fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/require"
)
Expand All @@ -20,13 +23,13 @@ func TestParseFillOriginData(t *testing.T) {
bi.SetUint64(val)
})

var data FillOriginData
var data bindings.ISolverNetFillOriginData
f.Fuzz(&data)

packed, err := inputsFillOriginData.Pack(data)
packed, err := solvernet.PackFillOriginData(data)
require.NoError(t, err)

parsed, err := parseFillOriginData(packed)
parsed, err := solvernet.ParseFillOriginData(packed)
require.NoError(t, err)

require.Equal(t, data, parsed)
Expand Down
51 changes: 51 additions & 0 deletions lib/contracts/solvernet/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package solvernet

import (
"strings"

"github.com/omni-network/omni/contracts/bindings"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
)

func DetectCustomError(custom error) string {
contracts := map[string]*bind.MetaData{
"inbox": bindings.SolverNetInboxMetaData,
"outbox": bindings.SolverNetOutboxMetaData,
"mock_vault": bindings.MockVaultMetaData,
"mock_token": bindings.MockTokenMetaData,
}

errMsg := custom.Error()

// errors from SafeTransferLib, not present in abis
safeTranserLibErrs := map[string]string{
"0x90b8ec18": "TransferFailed()",
"0x3e3f8f73": "ApproveFailed()",
"0x7939f424": "TransferFromFailed()",
"0xb12d13eb": "ETHTransferFailed()",
"0x54cd9435": "TotalSupplyQueryFailed()",
"0x6b836e6b": "Permit2Failed()",
"0x8757f0fd": "Permit2AmountOverflow()",
}

for id, msg := range safeTranserLibErrs {
if strings.Contains(errMsg, id) {
return "SafeTransferLib::" + msg
}
}

for name, contract := range contracts {
abi, err := contract.GetAbi()
if err != nil {
return "BUG"
}
for n, e := range abi.Errors {
if strings.Contains(errMsg, e.ID.Hex()[:10]) {
return name + "::" + n
}
}
}

return "unknown"
}
108 changes: 108 additions & 0 deletions lib/contracts/solvernet/order.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package solvernet

import (
"context"
"math"
"time"

"github.com/omni-network/omni/contracts/bindings"
"github.com/omni-network/omni/lib/cast"
"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/netconf"

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

var (
// SolverNetInbox.ORDER_DATA_TYPEHASH
// keccak256("OrderData(Call call,Deposit[] deposits)Call(uint64 chainId,bytes32 target,uint256 value,bytes data,TokenExpense[] expenses)TokenExpense(bytes32 token,bytes32 spender,uint256 amount)Deposit(bytes32 token,uint256 amount)").
orderDataTypeHash = cast.Must32(hexutil.MustDecode("0xe5be6bd381a38cd250f9aa1a05935cbcd261fe0e77e9ef6f6d07bf3b7e5d22e2"))
)

type OpenOpts struct {
FillDeadline time.Time
}

func WithFillDeadline(t time.Time) func(*OpenOpts) {
return func(o *OpenOpts) {
o.FillDeadline = t
}
}

func DefaultOpenOpts() *OpenOpts {
return &OpenOpts{
FillDeadline: time.Now().Add(24 * time.Hour),
}
}

// OpenOrder opens an order on chainID for user.
// user pays for the order, and must be in the backend for chainID.
func OpenOrder(
ctx context.Context,
network netconf.ID,
chainID uint64,
backends ethbackend.Backends,
user common.Address,
orderData bindings.ISolverNetOrderData,
opts ...func(*OpenOpts),
) error {
backend, err := backends.Backend(chainID)
if err != nil {
return errors.Wrap(err, "get backend")
}

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

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

contract, err := bindings.NewSolverNetInbox(addrs.SolverNetInbox, backend)
if err != nil {
return errors.Wrap(err, "bind contract")
}

o := DefaultOpenOpts()
for _, opt := range opts {
opt(o)
}

packed, err := PackOrderData(orderData)
if err != nil {
return errors.Wrap(err, "pack order data")
}

// fill deadline is currently not enforced by the contract
fillDeadline := o.FillDeadline.Unix()
if fillDeadline < time.Now().Unix() {
return errors.New("fill deadline must be in the future")
} else if fillDeadline > math.MaxUint32 {
return errors.New("fill deadline too far in the future")
}

order := bindings.IERC7683OnchainCrossChainOrder{
//nolint:gosec // overflow is checked above
FillDeadline: uint32(fillDeadline),
OrderData: packed,
OrderDataType: orderDataTypeHash,
}

tx, err := contract.Open(txOpts, order)
if err != nil {
return errors.Wrap(err, "open tx", "custom", DetectCustomError(err))
}

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

return nil
}
32 changes: 1 addition & 31 deletions solver/app/v2/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ const (
)

var (
bindingsABI = mustGetABI(bindings.ISolverNetBindingsMetaData)
inboxABI = mustGetABI(bindings.SolverNetInboxMetaData)
inboxABI = mustGetABI(bindings.SolverNetInboxMetaData)

// Event log topics (common.Hash).
topicOpened = mustGetEventTopic(inboxABI, "Open")
Expand All @@ -31,8 +30,6 @@ var (
topicReverted = mustGetEventTopic(inboxABI, "Reverted")
topicFilled = mustGetEventTopic(inboxABI, "Filled")
topicClaimed = mustGetEventTopic(inboxABI, "Claimed")

inputsFillOriginData = mustGetInputs(bindingsABI, "fillOriginData")
)

// eventMeta contains metadata about an event.
Expand Down Expand Up @@ -192,30 +189,3 @@ func mustGetEventTopic(abi *abi.ABI, name string) common.Hash {

return event.ID
}

// mustGetInputs returns the inputs for the method with the given name.
func mustGetInputs(abi *abi.ABI, name string) abi.Arguments {
method, ok := abi.Methods[name]
if !ok {
panic("method not found")
}

return method.Inputs
}

// parseFillOriginData parses FillOriginData from packed bytes.
func parseFillOriginData(data []byte) (FillOriginData, error) {
unpacked, err := inputsFillOriginData.Unpack(data)
if err != nil {
return FillOriginData{}, errors.Wrap(err, "unpack fill data")
}

wrap := struct {
Data FillOriginData
}{}
if err := inputsFillOriginData.Copy(&wrap, unpacked); err != nil {
return FillOriginData{}, errors.Wrap(err, "copy fill data")
}

return wrap.Data, nil
}
31 changes: 4 additions & 27 deletions solver/app/v2/procdeps.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package appv2
import (
"context"
"math/big"
"strings"

"github.com/omni-network/omni/contracts/bindings"
"github.com/omni-network/omni/lib/contracts/solvernet"
"github.com/omni-network/omni/lib/errors"
"github.com/omni-network/omni/lib/ethclient/ethbackend"
"github.com/omni-network/omni/lib/log"
Expand Down Expand Up @@ -135,7 +135,7 @@ func newFiller(
fillerData := []byte{} // fillerData is optional ERC7683 custom filler specific data, unused in our contracts
tx, err := outbox.Fill(txOpts, order.ID, order.FillOriginData, fillerData)
if err != nil {
return errors.Wrap(err, "fill order", "custom", detectCustomError(err))
return errors.Wrap(err, "fill order", "custom", solvernet.DetectCustomError(err))
} else if _, err := backend.WaitMined(ctx, tx); err != nil {
return errors.Wrap(err, "wait mined")
}
Expand All @@ -150,29 +150,6 @@ func newFiller(
}
}

func detectCustomError(custom error) string {
contracts := map[string]*bind.MetaData{
"inbox": bindings.SolveInboxMetaData,
"outbox": bindings.SolveOutboxMetaData,
"mock_vault": bindings.MockVaultMetaData,
"mock_token": bindings.MockTokenMetaData,
}

for name, contract := range contracts {
abi, err := contract.GetAbi()
if err != nil {
return "BUG"
}
for n, e := range abi.Errors {
if strings.Contains(custom.Error(), e.ID.Hex()[:10]) {
return name + "::" + n
}
}
}

return unknown
}

func approveOutboxSpend(ctx context.Context, output bindings.IERC7683Output, backend *ethbackend.Backend, solverAddr, outboxAddr common.Address) error {
if output.Token == [32]byte{} {
return errors.New("cannot approve native token")
Expand Down Expand Up @@ -258,7 +235,7 @@ func newRejector(

tx, err := inbox.Reject(txOpts, order.ID, uint8(reason))
if err != nil {
return errors.Wrap(err, "reject order", "custom", detectCustomError(err))
return errors.Wrap(err, "reject order", "custom", solvernet.DetectCustomError(err))
} else if _, err := backend.WaitMined(ctx, tx); err != nil {
return errors.Wrap(err, "wait mined")
}
Expand Down Expand Up @@ -290,7 +267,7 @@ func newAcceptor(

tx, err := inbox.Accept(txOpts, order.ID)
if err != nil {
return errors.Wrap(err, "accept order", "custom", detectCustomError(err))
return errors.Wrap(err, "accept order", "custom", solvernet.DetectCustomError(err))
} else if _, err := backend.WaitMined(ctx, tx); err != nil {
return errors.Wrap(err, "wait mined")
}
Expand Down
Loading
Loading