diff --git a/contracts/solve/script/MintMockToken.s.sol b/contracts/solve/script/MintMockToken.s.sol new file mode 100644 index 000000000..e2ecfeec7 --- /dev/null +++ b/contracts/solve/script/MintMockToken.s.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity =0.8.24; + +import { MockToken } from "test/utils/MockToken.sol"; +import { Script } from "forge-std/Script.sol"; + +contract MintMockToken is Script { + function run(address token, address to) public { + vm.startBroadcast(); + MockToken(token).mint(to, 1000 ether); + vm.stopBroadcast(); + } +} diff --git a/e2e/solve/deploy.go b/e2e/solve/deploy.go index 88b97215f..35e98fdf2 100644 --- a/e2e/solve/deploy.go +++ b/e2e/solve/deploy.go @@ -4,6 +4,7 @@ import ( "context" "github.com/omni-network/omni/e2e/solve/devapp" + "github.com/omni-network/omni/e2e/solve/symbiotic" "github.com/omni-network/omni/lib/contracts/solveinbox" "github.com/omni-network/omni/lib/contracts/solveoutbox" "github.com/omni-network/omni/lib/errors" @@ -22,22 +23,17 @@ func DeployContracts(ctx context.Context, network netconf.Network, backends ethb } log.Info(ctx, "Deploying solve contracts") + if err := deployBoxes(ctx, network, backends); err != nil { + return errors.Wrap(err, "deploy boxes") + } var eg errgroup.Group - - eg.Go(func() error { - 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) - }) - + eg.Go(func() error { return devapp.AllowOutboxCalls(ctx, network, backends) }) + eg.Go(func() error { return devapp.Deploy(ctx, network, backends) }) + eg.Go(func() error { return symbiotic.FundSolver(ctx, network.ID, backends) }) + eg.Go(func() error { return symbiotic.AllowOutboxCalls(ctx, network, backends) }) if err := eg.Wait(); err != nil { - return errors.Wrap(err, "deploy solver contracts") + return errors.Wrap(err, "setup solver devnet") } return nil diff --git a/e2e/solve/devapp/deploy.go b/e2e/solve/devapp/deploy.go index d6dde4bef..256dc4327 100644 --- a/e2e/solve/devapp/deploy.go +++ b/e2e/solve/devapp/deploy.go @@ -28,7 +28,7 @@ const ( // 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") + return errors.New("only devnet") } l1Backend, err := backends.Backend(static.L1.ChainID) @@ -91,7 +91,7 @@ func fundSolver(ctx context.Context, backend *ethbackend.Backend, tokenAddr comm // 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") + return errors.New("only devnet") } addrs, err := contracts.GetAddresses(ctx, network.ID) diff --git a/e2e/solve/symbiotic/app.go b/e2e/solve/symbiotic/app.go new file mode 100644 index 000000000..390d98f2f --- /dev/null +++ b/e2e/solve/symbiotic/app.go @@ -0,0 +1,157 @@ +package symbiotic + +import ( + "context" + + "github.com/omni-network/omni/contracts/bindings" + "github.com/omni-network/omni/e2e/app/eoa" + "github.com/omni-network/omni/e2e/solve/devapp" + "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/evmchain" + "github.com/omni-network/omni/lib/netconf" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + + _ "embed" +) + +type App struct { + L1wstETHCollateral common.Address + L1wstETH common.Address + L2wstETH common.Address + L1 evmchain.Metadata + L2 evmchain.Metadata +} + +var ( + //go:embed default-collateral-abi.json + collateralABIJSON []byte + + collateralABI = mustParseABI(collateralABIJSON) + depositABI = mustGetMethod(collateralABI, "deposit") + + mockL1 = mustChainMeta(evmchain.IDMockL1) // should be forked from holesky + mockL2 = mustChainMeta(evmchain.IDMockL2) + holesky = mustChainMeta(evmchain.IDHolesky) + baseSepolia = mustChainMeta(evmchain.IDBaseSepolia) +) + +func GetApp(network netconf.ID) (App, error) { + app := App{ + // holesky wsETH collateral + L1wstETHCollateral: common.HexToAddress("0x23e98253f372ee29910e22986fe75bb287b011fc"), + // holesky wsETH + L1wstETH: common.HexToAddress("0x8d09a4502cc8cf1547ad300e066060d043f6982d"), + // use mintable devapp mintable mock token, base sepolia has no canonical wsETH + L2wstETH: devapp.GetApp().L2Token, + } + + if network == netconf.Devnet { + app.L1 = mockL1 + app.L2 = mockL2 + + return app, nil + } + + if network == netconf.Staging { + app.L1 = holesky + app.L2 = baseSepolia + + return app, nil + } + + return App{}, errors.New("unsupported network", "network", network) +} + +func MustGetApp(network netconf.ID) App { + app, err := GetApp(network) + if err != nil { + panic(err) + } + + return app +} + +// AllowOutboxCalls allows the outbox to call the L1 wstETH collateral contract. +func AllowOutboxCalls(ctx context.Context, network netconf.Network, backends ethbackend.Backends) error { + app, err := GetApp(network.ID) + if err != nil { + return errors.Wrap(err, "get app") + } + + addrs, err := contracts.GetAddresses(ctx, network.ID) + if err != nil { + return errors.Wrap(err, "get addresses") + } + + l1Backend, err := backends.Backend(app.L1.ChainID) + if err != nil { + return errors.Wrap(err, "backend mock l1") + } + + if err := allowCalls(ctx, app, l1Backend, addrs.SolveOutbox); err != nil { + return errors.Wrap(err, "allow calls") + } + + return nil +} + +// allowCalls allows the outbox to call the L1 wstETH collateral deposit method. +func allowCalls(ctx context.Context, app App, backend *ethbackend.Backend, outboxAddr common.Address) error { + outbox, err := bindings.NewSolveOutbox(outboxAddr, backend) + if err != nil { + return errors.Wrap(err, "new solve outbox") + } + + manager := eoa.MustAddress(netconf.Devnet, eoa.RoleManager) + + txOpts, err := backend.BindOpts(ctx, manager) + if err != nil { + return errors.Wrap(err, "bind opts") + } + + depositID, err := cast.Array4(depositABI.ID[:4]) + if err != nil { + return err + } + + tx, err := outbox.SetAllowedCall(txOpts, app.L1wstETHCollateral, depositID, 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 +} + +func mustParseABI(json []byte) *abi.ABI { + var abi abi.ABI + if err := abi.UnmarshalJSON(json); err != nil { + panic(err) + } + + return &abi +} + +func mustGetMethod(abi *abi.ABI, name string) abi.Method { + method, ok := abi.Methods[name] + if !ok { + panic(errors.New("missing method", "name", name)) + } + + return method +} + +func mustChainMeta(chainID uint64) evmchain.Metadata { + meta, ok := evmchain.MetadataByID(chainID) + if !ok { + panic(errors.New("missing chain meta", "chain_id", chainID)) + } + + return meta +} diff --git a/e2e/solve/symbiotic/default-collateral-abi.json b/e2e/solve/symbiotic/default-collateral-abi.json new file mode 100644 index 000000000..d7e55bea6 --- /dev/null +++ b/e2e/solve/symbiotic/default-collateral-abi.json @@ -0,0 +1,655 @@ +[ + { + "type": "function", + "name": "allowance", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + }, + { + "name": "spender", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "approve", + "inputs": [ + { + "name": "spender", + "type": "address", + "internalType": "address" + }, + { + "name": "value", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "asset", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "balanceOf", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "debt", + "inputs": [ + { + "name": "issuer", + "type": "address", + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "deposit", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "deposit", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "v", + "type": "uint8", + "internalType": "uint8" + }, + { + "name": "r", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "s", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "increaseLimit", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "issueDebt", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "issuerDebt", + "inputs": [ + { + "name": "issuer", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "issuerRepaidDebt", + "inputs": [ + { + "name": "issuer", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "limit", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "limitIncreaser", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "recipientDebt", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "recipientRepaidDebt", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "repaidDebt", + "inputs": [ + { + "name": "issuer", + "type": "address", + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setLimitIncreaser", + "inputs": [ + { + "name": "limitIncreaser", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "totalDebt", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalRepaidDebt", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalSupply", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transfer", + "inputs": [ + { + "name": "to", + "type": "address", + "internalType": "address" + }, + { + "name": "value", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferFrom", + "inputs": [ + { + "name": "from", + "type": "address", + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "internalType": "address" + }, + { + "name": "value", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "withdraw", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "Approval", + "inputs": [ + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "spender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "value", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Deposit", + "inputs": [ + { + "name": "depositor", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "IncreaseLimit", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "IssueDebt", + "inputs": [ + { + "name": "issuer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "debtIssued", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RepayDebt", + "inputs": [ + { + "name": "issuer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "debtRepaid", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SetLimitIncreaser", + "inputs": [ + { + "name": "limitIncreaser", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Transfer", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "value", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Withdraw", + "inputs": [ + { + "name": "withdrawer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "ExceedsLimit", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientDeposit", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientIssueDebt", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientWithdraw", + "inputs": [] + }, + { + "type": "error", + "name": "NotLimitIncreaser", + "inputs": [] + } +] diff --git a/e2e/solve/symbiotic/fund.go b/e2e/solve/symbiotic/fund.go new file mode 100644 index 000000000..d0b668c93 --- /dev/null +++ b/e2e/solve/symbiotic/fund.go @@ -0,0 +1,33 @@ +package symbiotic + +import ( + "context" + + "github.com/omni-network/omni/e2e/app/eoa" + "github.com/omni-network/omni/lib/anvil" + "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/params" + + "cosmossdk.io/math" +) + +func FundSolver(ctx context.Context, network netconf.ID, backends ethbackend.Backends) error { + // funding solver with l1 wsETH uses anvil_setStorageAt util, which is only available on devnet + if network != netconf.Devnet { + return errors.New("only devnet") + } + + app := MustGetApp(network) + + ethCl, ok := backends.Clients()[app.L1.ChainID] + if !ok { + return errors.New("missing eth client", "chain", app.L1.Name) + } + + eth1m := math.NewInt(1_000_000).MulRaw(params.Ether).BigInt() + + return anvil.FundERC20(ctx, ethCl, app.L1wstETH, eth1m, eoa.MustAddress(netconf.Devnet, eoa.RoleSolver)) +} diff --git a/e2e/solve/symbiotic/target.go b/e2e/solve/symbiotic/target.go new file mode 100644 index 000000000..c401747ea --- /dev/null +++ b/e2e/solve/symbiotic/target.go @@ -0,0 +1,116 @@ +package symbiotic + +import ( + "bytes" + "context" + "math/big" + + "github.com/omni-network/omni/contracts/bindings" + "github.com/omni-network/omni/lib/errors" + "github.com/omni-network/omni/lib/log" + solver "github.com/omni-network/omni/solver/types" + + "github.com/ethereum/go-ethereum/common" +) + +var _ solver.Target = (*App)(nil) + +type DepositArgs struct { + Recipient common.Address + Amount *big.Int +} + +func (t App) ChainID() uint64 { + return t.L1.ChainID +} + +func (t App) Address() common.Address { + // Live ymbiotic deposits are for erc20 "collateral" wrappers, not vaults. + return t.L1wstETHCollateral +} + +func (t App) TokenPrereqs(call bindings.SolveCall) ([]bindings.SolveTokenPrereq, error) { + args, err := unpackDeposit(call.Data) + if err != nil { + return nil, errors.Wrap(err, "unpack deposit") + } + + return []bindings.SolveTokenPrereq{ + { + Token: t.L1wstETH, + Spender: t.L1wstETHCollateral, + Amount: args.Amount, + }, + }, nil +} + +func (t App) Verify(srcChainID uint64, call bindings.SolveCall, deposits []bindings.SolveDeposit) error { + // for now, we only accept deposits from a single, explicit l2 + if srcChainID != t.L2.ChainID { + return errors.New("source chain not supported", "src", srcChainID) + } + + args, err := unpackDeposit(call.Data) + if err != nil { + return errors.Wrap(err, "invalid deposit") + } + + if _, err := t.TokenPrereqs(call); err != nil { + return errors.Wrap(err, "token prereqs") + } + + var l2Deposit *bindings.SolveDeposit + for _, deposit := range deposits { + if deposit.Token == t.L2wstETH { + l2Deposit = &deposit + } + } + + // if no l2 deposit, we can'a accept + if l2Deposit == nil { + return errors.New("no L2 token deposit") + } + + // if l2 deposit amount does not match call amount, we can'a accept + if l2Deposit.Amount.Cmp(args.Amount) < 0 { + return errors.New("insufficient L2 token deposit", + "expected", args.Amount, + "actual", l2Deposit.Amount, + ) + } + + // TODO: require native deposit that covers gas / risk / overhead + + return nil +} + +func (App) DebugCall(ctx context.Context, call bindings.SolveCall) error { + dep, err := unpackDeposit(call.Data) + if err != nil { + return errors.Wrap(err, "unpack deposit") + } + + log.Debug(ctx, "DevSymbiotic", "method", "wstETH_collateral.deposit", + "recipient", dep.Recipient, "amount", dep.Amount) + + return nil +} + +func unpackDeposit(data []byte) (DepositArgs, error) { + trimmed := bytes.TrimPrefix(data, depositABI.ID) + if bytes.Equal(trimmed, data) { + return DepositArgs{}, errors.New("data not prefixed with deposit method id") + } + + unpacked, err := depositABI.Inputs.Unpack(trimmed) + if err != nil { + return DepositArgs{}, errors.Wrap(err, "unpack data") + } + + var args DepositArgs + if err := depositABI.Inputs.Copy(&args, unpacked); err != nil { + return DepositArgs{}, errors.Wrap(err, "copy args") + } + + return args, nil +} diff --git a/lib/anvil/utils.go b/lib/anvil/utils.go index 44cf5e74f..e6000a1e2 100644 --- a/lib/anvil/utils.go +++ b/lib/anvil/utils.go @@ -7,8 +7,10 @@ import ( "github.com/omni-network/omni/lib/errors" "github.com/omni-network/omni/lib/ethclient" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" ) // FundAccounts funds the anvil account via the anvil_setBalance RPC method. @@ -23,3 +25,40 @@ func FundAccounts(ctx context.Context, ethCl ethclient.Client, amount *big.Int, return nil } + +// FundERC20 funds the account with an ERC20 token balance. +// This only works on standard ERC20 tokens with _balances mapping at slot 0. +func FundERC20(ctx context.Context, client ethclient.Client, + token common.Address, amount *big.Int, accounts ...common.Address) error { + // storage value for _balances[address] + svalue := common.BigToHash(amount).Hex() + + for _, account := range accounts { + err := client.CallContext(ctx, nil, "anvil_setStorageAt", token, balanceSlot(account), svalue) + if err != nil { + return errors.Wrap(err, "fund erc20", "token", token, "account", account) + } + } + + return nil +} + +var ( + // _balances[account] storage slot == keccak256(abi.encode(account, idx)). + slotIdx = big.NewInt(0) + slotABI = abi.Arguments{ + {Type: abi.Type{T: abi.AddressTy}}, + {Type: abi.Type{T: abi.UintTy, Size: 256}}, + } +) + +// balanceSlot returns the storage slot _balances[account] for a standard ERC20 token (slot 0). +func balanceSlot(account common.Address) string { + slot, err := slotABI.Pack(account, slotIdx) + if err != nil { + // known args, this should never happen + panic(err) + } + + return hexutil.Encode(crypto.Keccak256(slot)) +} diff --git a/solver/app/targets.go b/solver/app/targets.go index 6afa9a6f3..910ea8765 100644 --- a/solver/app/targets.go +++ b/solver/app/targets.go @@ -3,13 +3,14 @@ package app import ( "github.com/omni-network/omni/contracts/bindings" "github.com/omni-network/omni/e2e/solve/devapp" + "github.com/omni-network/omni/e2e/solve/symbiotic" "github.com/omni-network/omni/lib/errors" "github.com/omni-network/omni/lib/netconf" "github.com/omni-network/omni/solver/types" ) var targetsByNetwork = map[netconf.ID][]types.Target{ - netconf.Devnet: {devapp.GetApp()}, + netconf.Devnet: {devapp.GetApp(), symbiotic.MustGetApp(netconf.Devnet)}, } // getTarget returns the target for the given network and call.