mirror of
https://github.com/AthanorLabs/atomic-swap.git
synced 2026-01-07 21:34:05 -05:00
feat: implement swap relayer cmd package (#282)
This commit is contained in:
@@ -9,9 +9,11 @@ import (
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/common"
|
||||
)
|
||||
@@ -104,3 +106,42 @@ func GetVersion() string {
|
||||
info.GoVersion,
|
||||
)
|
||||
}
|
||||
|
||||
// ReadUnsignedDecimalFlag reads a string flag and parses it into an *apd.Decimal.
|
||||
func ReadUnsignedDecimalFlag(ctx *cli.Context, flagName string) (*apd.Decimal, error) {
|
||||
s := ctx.String(flagName)
|
||||
if s == "" {
|
||||
return nil, fmt.Errorf("flag --%s cannot be empty", flagName)
|
||||
}
|
||||
bf, _, err := new(apd.Decimal).SetString(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid value %q for flag --%s", s, flagName)
|
||||
}
|
||||
if bf.IsZero() {
|
||||
return nil, fmt.Errorf("value of flag --%s cannot be zero", flagName)
|
||||
}
|
||||
if bf.Negative {
|
||||
return nil, fmt.Errorf("value of flag --%s cannot be negative", flagName)
|
||||
}
|
||||
|
||||
return bf, nil
|
||||
}
|
||||
|
||||
// ExpandBootnodes expands the boot nodes passed on the command line that
|
||||
// can be specified individually with multiple flags, but can also contain
|
||||
// multiple boot nodes passed to single flag separated by commas.
|
||||
func ExpandBootnodes(nodesCLI []string) []string {
|
||||
var nodes []string // nodes from all flag values combined
|
||||
for _, flagVal := range nodesCLI {
|
||||
splitNodes := strings.Split(flagVal, ",")
|
||||
for _, n := range splitNodes {
|
||||
n = strings.TrimSpace(n)
|
||||
// Handle the empty string to not use default bootnodes. Doing it here after
|
||||
// the split has the arguably positive side effect of skipping empty entries.
|
||||
if len(n) > 0 {
|
||||
nodes = append(nodes, strings.TrimSpace(n))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
@@ -81,3 +81,31 @@ func TestGetVersion(t *testing.T) {
|
||||
require.NotEmpty(t, GetVersion())
|
||||
t.Log(GetVersion())
|
||||
}
|
||||
|
||||
func Test_expandBootnodes(t *testing.T) {
|
||||
cliNodes := []string{
|
||||
" node1, node2 ,node3,node4 ",
|
||||
"node5",
|
||||
"\tnode6\n",
|
||||
"node7,node8",
|
||||
}
|
||||
expected := []string{
|
||||
"node1",
|
||||
"node2",
|
||||
"node3",
|
||||
"node4",
|
||||
"node5",
|
||||
"node6",
|
||||
"node7",
|
||||
"node8",
|
||||
}
|
||||
require.EqualValues(t, expected, ExpandBootnodes(cliNodes))
|
||||
}
|
||||
|
||||
func Test_expandBootnodes_noNodes(t *testing.T) {
|
||||
// This can happen when the user specifies a single `--bootnodes ""` flag
|
||||
// to not use the default bootnodes for an environment.
|
||||
cliNodes := []string{""}
|
||||
nodes := ExpandBootnodes(cliNodes)
|
||||
require.Zero(t, len(nodes))
|
||||
}
|
||||
|
||||
67
cmd/relayer/contract.go
Normal file
67
cmd/relayer/contract.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
rcommon "github.com/athanorlabs/go-relayer/common"
|
||||
rcontracts "github.com/athanorlabs/go-relayer/impls/gsnforwarder"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
|
||||
contracts "github.com/athanorlabs/atomic-swap/ethereum"
|
||||
)
|
||||
|
||||
func deployOrGetForwarder(
|
||||
ctx context.Context,
|
||||
addressString string,
|
||||
ec *ethclient.Client,
|
||||
key *rcommon.Key,
|
||||
chainID *big.Int,
|
||||
) (*rcontracts.IForwarder, ethcommon.Address, error) {
|
||||
txOpts, err := bind.NewKeyedTransactorWithChainID(key.PrivateKey(), chainID)
|
||||
if err != nil {
|
||||
return nil, ethcommon.Address{}, fmt.Errorf("failed to make transactor: %w", err)
|
||||
}
|
||||
|
||||
if addressString == "" {
|
||||
address, tx, _, err := rcontracts.DeployForwarder(txOpts, ec) //nolint:govet
|
||||
if err != nil {
|
||||
return nil, ethcommon.Address{}, err
|
||||
}
|
||||
|
||||
_, err = bind.WaitMined(ctx, ec, tx)
|
||||
if err != nil {
|
||||
return nil, ethcommon.Address{}, err
|
||||
}
|
||||
|
||||
log.Infof("deployed Forwarder.sol to %s", address)
|
||||
f, err := rcontracts.NewIForwarder(address, ec)
|
||||
if err != nil {
|
||||
return nil, ethcommon.Address{}, err
|
||||
}
|
||||
|
||||
return f, address, nil
|
||||
}
|
||||
|
||||
ok := ethcommon.IsHexAddress(addressString)
|
||||
if !ok {
|
||||
return nil, ethcommon.Address{}, errInvalidAddress
|
||||
}
|
||||
|
||||
address := ethcommon.HexToAddress(addressString)
|
||||
err = contracts.CheckForwarderContractCode(context.Background(), ec, address)
|
||||
if err != nil {
|
||||
return nil, ethcommon.Address{}, err
|
||||
}
|
||||
|
||||
log.Infof("loaded Forwarder.sol at %s", address)
|
||||
f, err := rcontracts.NewIForwarder(address, ec)
|
||||
if err != nil {
|
||||
return nil, ethcommon.Address{}, err
|
||||
}
|
||||
|
||||
return f, address, nil
|
||||
}
|
||||
388
cmd/relayer/main.go
Normal file
388
cmd/relayer/main.go
Normal file
@@ -0,0 +1,388 @@
|
||||
// Package main is the entrypoint for the swap-specific Ethereum transaction relayer.
|
||||
// Its purpose is to allow swaps users (ETH-takers in particular) to submit calls to the
|
||||
// swap contract to claim their ETH from an account that does not have any ETH in it.
|
||||
// This improves the swap UX by allowing users to obtain ETH without already having any.
|
||||
// In this case, the relayer submits the transaction on the user's behalf, paying their
|
||||
// gas fees, and (optionally) receiving a small percentage of the swap's value as payment.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
p2pnet "github.com/athanorlabs/go-p2p-net"
|
||||
rcommon "github.com/athanorlabs/go-relayer/common"
|
||||
rcontracts "github.com/athanorlabs/go-relayer/impls/gsnforwarder"
|
||||
net "github.com/athanorlabs/go-relayer/net"
|
||||
"github.com/athanorlabs/go-relayer/relayer"
|
||||
rrpc "github.com/athanorlabs/go-relayer/rpc"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/cliutil"
|
||||
"github.com/athanorlabs/atomic-swap/common"
|
||||
swapnet "github.com/athanorlabs/atomic-swap/net"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
)
|
||||
|
||||
const (
|
||||
flagDataDir = "data-dir"
|
||||
flagEthereumEndpoint = "ethereum-endpoint"
|
||||
flagForwarderAddress = "forwarder-address"
|
||||
flagKey = "key"
|
||||
flagRPC = "rpc"
|
||||
flagRPCPort = "rpc-port"
|
||||
flagDeploy = "deploy"
|
||||
flagLog = "log-level"
|
||||
// TODO: do we need this, or can we assume all swap relayers will need to be on the p2p network?
|
||||
flagWithNetwork = "with-network"
|
||||
flagLibp2pKey = "libp2p-key"
|
||||
flagLibp2pPort = "libp2p-port"
|
||||
flagBootnodes = "bootnodes"
|
||||
flagRelayerCommission = "relayer-commission"
|
||||
|
||||
defaultLibp2pPort = 10900
|
||||
)
|
||||
|
||||
var (
|
||||
log = logging.Logger("main")
|
||||
|
||||
flags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: flagDataDir,
|
||||
Usage: "Path to store swap artifacts",
|
||||
Value: "{HOME}/.atomicswap/{ENV}", // For --help only, actual default replaces variables
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagEthereumEndpoint,
|
||||
Value: "http://localhost:8545",
|
||||
Usage: "Ethereum RPC endpoint",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagKey,
|
||||
Value: fmt.Sprintf("{DATA-DIR}/%s", common.DefaultEthKeyFileName),
|
||||
Usage: "Path to file containing Ethereum private key",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagForwarderAddress,
|
||||
Usage: "Address of the forwarder contract to use. Defaults to the forwarder address in the chain's swap contract.",
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flagRPCPort,
|
||||
Value: 7799,
|
||||
Usage: "Relayer RPC server port",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flagRPC,
|
||||
Value: false,
|
||||
Usage: "Run the relayer HTTP-RPC server on localhost. Defaults to false",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flagDeploy,
|
||||
Usage: "Deploy an instance of the forwarder contract",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagLog,
|
||||
Value: "info",
|
||||
Usage: "Set log level: one of [error|warn|info|debug]",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flagWithNetwork,
|
||||
Value: true,
|
||||
Usage: "Run the relayer with p2p network capabilities",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagLibp2pKey,
|
||||
Usage: "libp2p private key",
|
||||
Value: fmt.Sprintf("{DATA_DIR}/%s", common.DefaultLibp2pKeyFileName),
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flagLibp2pPort,
|
||||
Usage: "libp2p port to listen on",
|
||||
Value: defaultLibp2pPort,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: flagBootnodes,
|
||||
Aliases: []string{"bn"},
|
||||
Usage: "libp2p bootnode, comma separated if passing multiple to a single flag",
|
||||
EnvVars: []string{"SWAPD_BOOTNODES"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagRelayerCommission,
|
||||
Usage: "Minimum commission percentage (of the swap value) to receive:" +
|
||||
" eg. --relayer-commission=0.01 for 1% commission",
|
||||
Value: common.DefaultRelayerCommission.Text('f'),
|
||||
},
|
||||
}
|
||||
|
||||
errInvalidAddress = errors.New("invalid forwarder address")
|
||||
errNoEthereumPrivateKey = errors.New("must provide ethereum private key with --key")
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "relayer",
|
||||
Usage: "Ethereum transaction relayer",
|
||||
Version: cliutil.GetVersion(),
|
||||
Flags: flags,
|
||||
Action: run,
|
||||
EnableBashCompletion: true,
|
||||
Suggest: true,
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func setLogLevels(c *cli.Context) error {
|
||||
const (
|
||||
levelError = "error"
|
||||
levelWarn = "warn"
|
||||
levelInfo = "info"
|
||||
levelDebug = "debug"
|
||||
)
|
||||
|
||||
level := c.String(flagLog)
|
||||
switch level {
|
||||
case levelError, levelWarn, levelInfo, levelDebug:
|
||||
default:
|
||||
return fmt.Errorf("invalid log level %q", level)
|
||||
}
|
||||
|
||||
_ = logging.SetLogLevel("main", level)
|
||||
_ = logging.SetLogLevel("relayer", level)
|
||||
_ = logging.SetLogLevel("rpc", level)
|
||||
_ = logging.SetLogLevel("p2pnet", "debug")
|
||||
return nil
|
||||
}
|
||||
|
||||
func run(c *cli.Context) error {
|
||||
err := setLogLevels(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
port := uint16(c.Uint(flagRPCPort))
|
||||
endpoint := c.String(flagEthereumEndpoint)
|
||||
ec, err := ethclient.Dial(endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(c.Context)
|
||||
defer cancel()
|
||||
|
||||
chainID, err := ec.ChainID(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("starting relayer with ethereum endpoint %s and chain ID %s", endpoint, chainID)
|
||||
|
||||
config, err := common.ConfigFromChainID(chainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// cfg.DataDir already has a default set, so only override if the user explicitly set the flag
|
||||
var datadir string
|
||||
if c.IsSet(flagDataDir) {
|
||||
datadir = c.String(flagDataDir) // override the value derived from `flagEnv`
|
||||
} else {
|
||||
datadir = config.DataDir
|
||||
}
|
||||
datadir = path.Join(datadir, "relayer")
|
||||
if err = common.MakeDir(datadir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyFile := path.Join(datadir, common.DefaultEthKeyFileName)
|
||||
if c.IsSet(flagKey) {
|
||||
keyFile = c.String(flagKey)
|
||||
}
|
||||
|
||||
key, err := getPrivateKey(keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set the forwarder address from the config, if it exists on that network
|
||||
// however, if the --forwarder-address flag is set, that address takes precedence
|
||||
var contractAddr string
|
||||
if (config.ContractAddress != ethcommon.Address{}) {
|
||||
contractAddr = config.ForwarderContractAddress.String()
|
||||
}
|
||||
addrFromFlag := c.String(flagForwarderAddress)
|
||||
if addrFromFlag != "" {
|
||||
contractAddr = addrFromFlag
|
||||
}
|
||||
|
||||
deploy := c.Bool(flagDeploy)
|
||||
if deploy && (addrFromFlag != "") {
|
||||
return fmt.Errorf("flags --%s and --%s are mutually exclusive", flagDeploy, flagForwarderAddress)
|
||||
}
|
||||
if !deploy && (contractAddr == "") {
|
||||
return fmt.Errorf("either --%s or --%s is required", flagDeploy, flagForwarderAddress)
|
||||
}
|
||||
|
||||
forwarder, forwarderAddr, err := deployOrGetForwarder(
|
||||
ctx,
|
||||
contractAddr,
|
||||
ec,
|
||||
key,
|
||||
chainID,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
relayerCommission, err := cliutil.ReadUnsignedDecimalFlag(c, flagRelayerCommission)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if relayerCommission.Cmp(apd.New(1, -1)) > 0 {
|
||||
return errors.New("relayer commission is too high: must be less than 0.1 (10%)")
|
||||
}
|
||||
|
||||
// TODO: do we need to restrict potential commission values? eg. 1%, 1.25%, 1.5%, etc
|
||||
// or should we just require a fixed value for now?
|
||||
v := &validator{
|
||||
ctx: ctx,
|
||||
ec: ec,
|
||||
relayerCommission: relayerCommission,
|
||||
forwarderAddress: forwarderAddr,
|
||||
}
|
||||
|
||||
// the forwarder contract is fixed here; thus it needs to be the same
|
||||
// as what's hardcoded in the swap contract addr for that network.
|
||||
rcfg := &relayer.Config{
|
||||
Ctx: ctx,
|
||||
EthClient: ec,
|
||||
Forwarder: rcontracts.NewIForwarderWrapped(forwarder),
|
||||
Key: key,
|
||||
ValidateTransactionFunc: v.validateTransactionFunc,
|
||||
}
|
||||
|
||||
r, err := relayer.NewRelayer(rcfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Bool(flagWithNetwork) {
|
||||
h, err := setupNetwork(ctx, c, ec, r, datadir) //nolint:govet
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = h.Stop()
|
||||
}()
|
||||
}
|
||||
|
||||
if c.Bool(flagRPC) {
|
||||
go signalHandler(ctx, cancel)
|
||||
rpcCfg := &rrpc.Config{
|
||||
Ctx: ctx,
|
||||
Address: fmt.Sprintf("127.0.0.1:%d", port),
|
||||
Relayer: r,
|
||||
}
|
||||
|
||||
server, err := rrpc.NewServer(rpcCfg) //nolint:govet
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = server.Start()
|
||||
if errors.Is(err, context.Canceled) || errors.Is(err, http.ErrServerClosed) {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
signalHandler(ctx, cancel)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func setupNetwork(
|
||||
ctx context.Context,
|
||||
c *cli.Context,
|
||||
ec *ethclient.Client,
|
||||
r *relayer.Relayer,
|
||||
datadir string,
|
||||
) (*net.Host, error) {
|
||||
chainID, err := ec.ChainID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var bootnodes []string
|
||||
if c.IsSet(flagBootnodes) {
|
||||
bootnodes = cliutil.ExpandBootnodes(c.StringSlice(flagBootnodes))
|
||||
}
|
||||
|
||||
libp2pKey := path.Join(datadir, common.DefaultLibp2pKeyFileName)
|
||||
if c.IsSet(flagLibp2pKey) {
|
||||
libp2pKey = c.String(flagLibp2pKey)
|
||||
if libp2pKey == "" {
|
||||
return nil, errFlagValueEmpty(flagLibp2pKey)
|
||||
}
|
||||
}
|
||||
|
||||
listenIP := "0.0.0.0"
|
||||
netCfg := &p2pnet.Config{
|
||||
Ctx: ctx,
|
||||
DataDir: datadir,
|
||||
Port: uint16(c.Uint(flagLibp2pPort)),
|
||||
KeyFile: libp2pKey,
|
||||
Bootnodes: bootnodes,
|
||||
ProtocolID: fmt.Sprintf("/%s/%d/%s", swapnet.ProtocolID, chainID.Int64(), net.ProtocolID),
|
||||
ListenIP: listenIP,
|
||||
}
|
||||
|
||||
cfg := &net.Config{
|
||||
Context: ctx,
|
||||
P2pConfig: netCfg,
|
||||
TransactionSubmitter: r,
|
||||
IsRelayer: true,
|
||||
}
|
||||
|
||||
h, err := net.NewHost(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = h.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func getPrivateKey(keyFile string) (*rcommon.Key, error) {
|
||||
if keyFile != "" {
|
||||
fileData, err := os.ReadFile(filepath.Clean(keyFile))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read private key file: %w", err)
|
||||
}
|
||||
keyHex := strings.TrimSpace(string(fileData))
|
||||
return rcommon.NewKeyFromPrivateKeyString(keyHex)
|
||||
}
|
||||
return nil, errNoEthereumPrivateKey
|
||||
}
|
||||
|
||||
func errFlagValueEmpty(flag string) error {
|
||||
return fmt.Errorf("flag %q requires a non-empty value", flag)
|
||||
}
|
||||
28
cmd/relayer/signal_handler.go
Normal file
28
cmd/relayer/signal_handler.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func signalHandler(ctx context.Context, cancel context.CancelFunc) {
|
||||
sigc := make(chan os.Signal, 1)
|
||||
signal.Ignore(syscall.SIGHUP)
|
||||
signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
|
||||
defer func() {
|
||||
// Hopefully, we'll exit our main() before this sleep ends, but if not we allow
|
||||
// the default signal behavior to kill us after this function exits.
|
||||
time.Sleep(1 * time.Second)
|
||||
signal.Stop(sigc)
|
||||
}()
|
||||
|
||||
select {
|
||||
case s := <-sigc:
|
||||
log.Infof("Received signal %s(%d), shutting down...", s, s)
|
||||
cancel()
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
165
cmd/relayer/validate.go
Normal file
165
cmd/relayer/validate.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/coins"
|
||||
contracts "github.com/athanorlabs/atomic-swap/ethereum"
|
||||
rcommon "github.com/athanorlabs/go-relayer/common"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
)
|
||||
|
||||
var (
|
||||
uint256Ty, _ = abi.NewType("uint256", "", nil)
|
||||
bytes32Ty, _ = abi.NewType("bytes32", "", nil)
|
||||
addressTy, _ = abi.NewType("address", "", nil)
|
||||
arguments = abi.Arguments{
|
||||
{
|
||||
Name: "owner",
|
||||
Type: addressTy,
|
||||
},
|
||||
{
|
||||
Name: "claimer",
|
||||
Type: addressTy,
|
||||
},
|
||||
{
|
||||
Name: "pubKeyClaim",
|
||||
Type: bytes32Ty,
|
||||
},
|
||||
{
|
||||
Name: "pubKeyRefund",
|
||||
Type: bytes32Ty,
|
||||
},
|
||||
{
|
||||
Name: "timeout0",
|
||||
Type: uint256Ty,
|
||||
},
|
||||
{
|
||||
Name: "timeout1",
|
||||
Type: uint256Ty,
|
||||
},
|
||||
{
|
||||
Name: "asset",
|
||||
Type: addressTy,
|
||||
},
|
||||
{
|
||||
Name: "value",
|
||||
Type: uint256Ty,
|
||||
},
|
||||
{
|
||||
Name: "nonce",
|
||||
Type: uint256Ty,
|
||||
},
|
||||
{
|
||||
Name: "_s",
|
||||
Type: bytes32Ty,
|
||||
},
|
||||
{
|
||||
Name: "fee",
|
||||
Type: uint256Ty,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type validator struct {
|
||||
ctx context.Context
|
||||
ec *ethclient.Client
|
||||
relayerCommission *apd.Decimal
|
||||
forwarderAddress ethcommon.Address
|
||||
}
|
||||
|
||||
func (v *validator) validateTransactionFunc(req *rcommon.SubmitTransactionRequest) error {
|
||||
// validate that:
|
||||
// 1. the `to` address is a swap contract;
|
||||
// 2. the function being called is `claimRelayer`;
|
||||
// 3. the fee passed to `claimRelayer` is equal to or greater
|
||||
// than our desired commission percentage.
|
||||
|
||||
forwarderAddr, err := contracts.CheckSwapFactoryContractCode(
|
||||
v.ctx, v.ec, req.To,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if forwarderAddr != v.forwarderAddress {
|
||||
return fmt.Errorf("swap contract does not have expected forwarder address: got %s, expected %s",
|
||||
forwarderAddr,
|
||||
v.forwarderAddress,
|
||||
)
|
||||
}
|
||||
|
||||
// hardcoded, from swap_factory.go bindings
|
||||
claimRelayerSig := ethcommon.FromHex("0x73e4771c")
|
||||
if !bytes.Equal(claimRelayerSig, req.Data[:4]) {
|
||||
return fmt.Errorf("call must be to claimRelayer(); got call to function with sig 0x%x", req.Data[:4])
|
||||
}
|
||||
|
||||
args, err := unpackData(req.Data[4:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = validateRelayerFee(args, v.relayerCommission)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unpackData(data []byte) (map[string]interface{}, error) {
|
||||
args := make(map[string]interface{})
|
||||
err := arguments.UnpackIntoMap(args, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func validateRelayerFee(args map[string]interface{}, minFeePercentage *apd.Decimal) error {
|
||||
value, ok := args["value"].(*big.Int)
|
||||
if !ok {
|
||||
// this shouldn't happen afaik
|
||||
return errors.New("value argument was not marshalled into a *big.Int")
|
||||
}
|
||||
|
||||
fee, ok := args["fee"].(*big.Int)
|
||||
if !ok {
|
||||
// this shouldn't happen afaik
|
||||
return errors.New("fee argument was not marshalled into a *big.Int")
|
||||
}
|
||||
|
||||
valueD := apd.NewWithBigInt(
|
||||
new(apd.BigInt).SetMathBigInt(value), // swap value, in wei
|
||||
0,
|
||||
)
|
||||
feeD := apd.NewWithBigInt(
|
||||
new(apd.BigInt).SetMathBigInt(fee), // fee, in wei
|
||||
0,
|
||||
)
|
||||
|
||||
percentage := new(apd.Decimal)
|
||||
_, err := coins.DecimalCtx().Quo(percentage, feeD, valueD)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if percentage.Cmp(minFeePercentage) < 0 {
|
||||
return fmt.Errorf("fee too low: percentage is %s, expected minimum %s",
|
||||
percentage,
|
||||
minFeePercentage,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
96
cmd/relayer/validate_test.go
Normal file
96
cmd/relayer/validate_test.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
contracts "github.com/athanorlabs/atomic-swap/ethereum"
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestValidateRelayerFee(t *testing.T) {
|
||||
swapABI, err := abi.JSON(strings.NewReader(contracts.SwapFactoryMetaData.ABI))
|
||||
require.NoError(t, err)
|
||||
|
||||
type testCase struct {
|
||||
value, fee *big.Int
|
||||
minFeePercentage *apd.Decimal
|
||||
expectErr bool
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
value: big.NewInt(100),
|
||||
fee: big.NewInt(1),
|
||||
minFeePercentage: apd.New(1, -2),
|
||||
},
|
||||
{
|
||||
value: big.NewInt(100),
|
||||
fee: big.NewInt(2),
|
||||
minFeePercentage: apd.New(1, -2),
|
||||
},
|
||||
{
|
||||
value: big.NewInt(1000),
|
||||
fee: big.NewInt(1),
|
||||
minFeePercentage: apd.New(1, -2),
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
value: big.NewInt(100),
|
||||
fee: big.NewInt(100),
|
||||
minFeePercentage: apd.New(1, 0),
|
||||
},
|
||||
{
|
||||
value: big.NewInt(100),
|
||||
fee: big.NewInt(10),
|
||||
minFeePercentage: apd.New(1, -1),
|
||||
},
|
||||
{
|
||||
value: big.NewInt(10000),
|
||||
fee: big.NewInt(99),
|
||||
minFeePercentage: apd.New(1, -2),
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
value: big.NewInt(10000),
|
||||
fee: big.NewInt(101),
|
||||
minFeePercentage: apd.New(1, -2),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
args := []interface{}{
|
||||
&contracts.SwapFactorySwap{
|
||||
Owner: ethcommon.Address{},
|
||||
Claimer: ethcommon.Address{},
|
||||
PubKeyClaim: [32]byte{},
|
||||
PubKeyRefund: [32]byte{},
|
||||
Timeout0: new(big.Int),
|
||||
Timeout1: new(big.Int),
|
||||
Asset: ethcommon.Address{},
|
||||
Value: tc.value,
|
||||
Nonce: new(big.Int),
|
||||
},
|
||||
[32]byte{},
|
||||
tc.fee,
|
||||
}
|
||||
data, err := swapABI.Pack("claimRelayer", args...)
|
||||
require.NoError(t, err)
|
||||
|
||||
unpacked, err := unpackData(data[4:])
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, unpacked["value"], tc.value)
|
||||
require.Equal(t, unpacked["fee"], tc.fee)
|
||||
|
||||
err = validateRelayerFee(unpacked, tc.minFeePercentage)
|
||||
if tc.expectErr {
|
||||
require.Error(t, err)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
@@ -473,17 +473,17 @@ func runQueryAll(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
func runMake(ctx *cli.Context) error {
|
||||
min, err := readUnsignedDecimalFlag(ctx, flagMinAmount)
|
||||
min, err := cliutil.ReadUnsignedDecimalFlag(ctx, flagMinAmount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
max, err := readUnsignedDecimalFlag(ctx, flagMaxAmount)
|
||||
max, err := cliutil.ReadUnsignedDecimalFlag(ctx, flagMaxAmount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exchangeRateDec, err := readUnsignedDecimalFlag(ctx, flagExchangeRate)
|
||||
exchangeRateDec, err := cliutil.ReadUnsignedDecimalFlag(ctx, flagExchangeRate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -509,7 +509,7 @@ func runMake(ctx *cli.Context) error {
|
||||
relayerEndpoint := ctx.String(flagRelayerEndpoint)
|
||||
relayerCommission := new(apd.Decimal)
|
||||
if relayerEndpoint != "" {
|
||||
if relayerCommission, err = readUnsignedDecimalFlag(ctx, flagRelayerCommission); err != nil {
|
||||
if relayerCommission, err = cliutil.ReadUnsignedDecimalFlag(ctx, flagRelayerCommission); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if ctx.IsSet(flagRelayerCommission) {
|
||||
@@ -577,7 +577,7 @@ func runTake(ctx *cli.Context) error {
|
||||
return errInvalidFlagValue(flagOfferID, err)
|
||||
}
|
||||
|
||||
providesAmount, err := readUnsignedDecimalFlag(ctx, flagProvidesAmount)
|
||||
providesAmount, err := cliutil.ReadUnsignedDecimalFlag(ctx, flagProvidesAmount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -850,22 +850,3 @@ func providesStrToVal(providesStr string) (coins.ProvidesCoin, error) {
|
||||
}
|
||||
return coins.NewProvidesCoin(providesStr)
|
||||
}
|
||||
|
||||
func readUnsignedDecimalFlag(ctx *cli.Context, flagName string) (*apd.Decimal, error) {
|
||||
s := ctx.String(flagName)
|
||||
if s == "" {
|
||||
return nil, fmt.Errorf("flag --%s cannot be empty", flagName)
|
||||
}
|
||||
bf, _, err := new(apd.Decimal).SetString(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid value %q for flag --%s", s, flagName)
|
||||
}
|
||||
if bf.IsZero() {
|
||||
return nil, fmt.Errorf("value of flag --%s cannot be zero", flagName)
|
||||
}
|
||||
if bf.Negative {
|
||||
return nil, fmt.Errorf("value of flag --%s cannot be negative", flagName)
|
||||
}
|
||||
|
||||
return bf, nil
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/ChainSafe/chaindb"
|
||||
p2pnet "github.com/athanorlabs/go-p2p-net"
|
||||
@@ -347,25 +346,6 @@ func (d *daemon) stop() error {
|
||||
}
|
||||
}
|
||||
|
||||
// expandBootnodes expands the boot nodes passed on the command line that
|
||||
// can be specified individually with multiple flags, but can also contain
|
||||
// multiple boot nodes passed to single flag separated by commas.
|
||||
func expandBootnodes(nodesCLI []string) []string {
|
||||
var nodes []string // nodes from all flag values combined
|
||||
for _, flagVal := range nodesCLI {
|
||||
splitNodes := strings.Split(flagVal, ",")
|
||||
for _, n := range splitNodes {
|
||||
n = strings.TrimSpace(n)
|
||||
// Handle the empty string to not use default bootnodes. Doing it here after
|
||||
// the split has the arguably positive side effect of skipping empty entries.
|
||||
if len(n) > 0 {
|
||||
nodes = append(nodes, strings.TrimSpace(n))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (d *daemon) make(c *cli.Context) error { //nolint:gocyclo
|
||||
env, err := common.NewEnv(c.String(flagEnv))
|
||||
if err != nil {
|
||||
@@ -399,7 +379,7 @@ func (d *daemon) make(c *cli.Context) error { //nolint:gocyclo
|
||||
}
|
||||
|
||||
if c.IsSet(flagBootnodes) {
|
||||
cfg.Bootnodes = expandBootnodes(c.StringSlice(flagBootnodes))
|
||||
cfg.Bootnodes = cliutil.ExpandBootnodes(c.StringSlice(flagBootnodes))
|
||||
}
|
||||
|
||||
libp2pKey := cfg.LibP2PKeyFile()
|
||||
|
||||
@@ -133,34 +133,6 @@ func TestDaemon_DevXMRMaker(t *testing.T) {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func Test_expandBootnodes(t *testing.T) {
|
||||
cliNodes := []string{
|
||||
" node1, node2 ,node3,node4 ",
|
||||
"node5",
|
||||
"\tnode6\n",
|
||||
"node7,node8",
|
||||
}
|
||||
expected := []string{
|
||||
"node1",
|
||||
"node2",
|
||||
"node3",
|
||||
"node4",
|
||||
"node5",
|
||||
"node6",
|
||||
"node7",
|
||||
"node8",
|
||||
}
|
||||
require.EqualValues(t, expected, expandBootnodes(cliNodes))
|
||||
}
|
||||
|
||||
func Test_expandBootnodes_noNodes(t *testing.T) {
|
||||
// This can happen when the user specifies a single `--bootnodes ""` flag
|
||||
// to not use the default bootnodes for an environment.
|
||||
cliNodes := []string{""}
|
||||
nodes := expandBootnodes(cliNodes)
|
||||
require.Zero(t, len(nodes))
|
||||
}
|
||||
|
||||
func TestDaemon_PersistOffers(t *testing.T) {
|
||||
startupTimeout := time.Millisecond * 100
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// DecimalCtx is the apd context used for math operations on our coins
|
||||
// decimalCtx is the apd context used for math operations on our coins
|
||||
decimalCtx = apd.BaseContext.WithPrecision(MaxCoinPrecision)
|
||||
|
||||
log = logging.Logger("coins")
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
@@ -30,10 +32,11 @@ type MoneroNode struct {
|
||||
|
||||
// Config contains constants that are defaults for various environments
|
||||
type Config struct {
|
||||
DataDir string
|
||||
MoneroNodes []*MoneroNode
|
||||
ContractAddress ethcommon.Address
|
||||
Bootnodes []string
|
||||
DataDir string
|
||||
MoneroNodes []*MoneroNode
|
||||
ContractAddress ethcommon.Address
|
||||
ForwarderContractAddress ethcommon.Address
|
||||
Bootnodes []string
|
||||
}
|
||||
|
||||
// MainnetConfig is the mainnet ethereum and monero configuration
|
||||
@@ -76,7 +79,8 @@ var StagenetConfig = Config{
|
||||
Port: 38081,
|
||||
},
|
||||
},
|
||||
ContractAddress: ethcommon.HexToAddress("0x01EeB71A63853fc89Ef26493bbdB7829F72b40d4"),
|
||||
ContractAddress: ethcommon.HexToAddress("0x55c29ed1D31FC511c425308A03c060238b7aC35A"),
|
||||
ForwarderContractAddress: ethcommon.HexToAddress("0xbb6a65B366251D98cDe86692ff16B54d3d9e804d"),
|
||||
Bootnodes: []string{
|
||||
"/ip4/134.122.115.208/tcp/9900/p2p/12D3KooWDqCzbjexHEa8Rut7bzxHFpRMZyDRW1L6TGkL1KY24JH5",
|
||||
"/ip4/143.198.123.27/tcp/9900/p2p/12D3KooWSc4yFkPWBFmPToTMbhChH3FAgGH96DNzSg5fio1pQYoN",
|
||||
@@ -158,3 +162,17 @@ func DefaultMoneroPortFromEnv(env Environment) uint {
|
||||
panic("invalid environment")
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigFromChainID returns the *Config corresponding to the given chain ID.
|
||||
func ConfigFromChainID(chainID *big.Int) (Config, error) {
|
||||
switch chainID.Uint64() {
|
||||
case MainnetChainID:
|
||||
return MainnetConfig, nil
|
||||
case GoerliChainID:
|
||||
return StagenetConfig, nil
|
||||
case GanacheChainID, HardhatChainID:
|
||||
return DevelopmentConfig, nil
|
||||
default:
|
||||
return Config{}, fmt.Errorf("no config for chain ID %d", chainID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Package common is for miscellaneous constants, types and interfaces used by many packages.
|
||||
package common
|
||||
|
||||
import "github.com/cockroachdb/apd/v3"
|
||||
|
||||
const (
|
||||
DefaultMoneroDaemonMainnetPort = 18081 //nolint
|
||||
DefaultMoneroDaemonDevPort = DefaultMoneroDaemonMainnetPort
|
||||
@@ -30,3 +32,7 @@ const (
|
||||
GanacheChainID = 1337
|
||||
HardhatChainID = 31337
|
||||
)
|
||||
|
||||
// DefaultRelayerCommission is the default commission percentage for swap relayers.
|
||||
// It's set to 0.01 or 1%.
|
||||
var DefaultRelayerCommission = apd.New(1, -2)
|
||||
|
||||
@@ -40,7 +40,7 @@ file above. More information on what the individual files contain can be
|
||||
|
||||
### {DATA_DIR}/eth.key
|
||||
|
||||
This is the default location of your ethereum private key used by swaps. Alternate
|
||||
This is the default location of your Ethereum private key used by swaps. Alternate
|
||||
locations can be configured with `--ethereum-privkey`. If the file does not
|
||||
exist, a new random key will be created and placed in this location.
|
||||
|
||||
@@ -63,3 +63,19 @@ Stores information on a swap when it reaches the stage where ethereum is locked.
|
||||
|
||||
Only written when `--deploy` is passed to swapd. This file stores the address
|
||||
that the contract was deployed to along with other data.
|
||||
|
||||
## Relayer default file locations
|
||||
|
||||
### {DATA_DIR}/relayer
|
||||
|
||||
By default, all relayer-related files will be placed in the `relayer` directory within the data dir.
|
||||
|
||||
### {DATA_DIR}/relayer/eth.key
|
||||
|
||||
The location of the Ethereum private key used by the relayer to submit transactions. Fees received by the relayer will also go into this account. Alternate locations can be configured with `--ethereum-privkey`. If the file does not exist, the relayer will error on startup.
|
||||
|
||||
### {DATA_DIR}/relayer/net.key
|
||||
|
||||
The private key to the relayer's libp2p identity. If the file does not exist, a new
|
||||
random key will be generated and placed in this location. Alternate locations can be
|
||||
configured with `--libp2p-key`. It does not necessarily need to be a different key than that used by swapd.
|
||||
@@ -85,7 +85,7 @@ func CheckSwapFactoryContractCode(
|
||||
return forwarderAddress, nil
|
||||
}
|
||||
|
||||
err = checkForwarderContractCode(ctx, ec, forwarderAddress)
|
||||
err = CheckForwarderContractCode(ctx, ec, forwarderAddress)
|
||||
if err != nil {
|
||||
return ethcommon.Address{}, err
|
||||
}
|
||||
@@ -94,9 +94,9 @@ func CheckSwapFactoryContractCode(
|
||||
return forwarderAddress, nil
|
||||
}
|
||||
|
||||
// checkSwapFactoryForwarder checks that the trusted forwarder contract used by
|
||||
// CheckForwarderContractCode checks that the trusted forwarder contract used by
|
||||
// the given swap contract has the expected bytecode.
|
||||
func checkForwarderContractCode(
|
||||
func CheckForwarderContractCode(
|
||||
ctx context.Context,
|
||||
ec *ethclient.Client,
|
||||
contractAddr ethcommon.Address,
|
||||
|
||||
@@ -52,7 +52,7 @@ func TestCheckForwarderContractCode(t *testing.T) {
|
||||
ec, _ := tests.NewEthClient(t)
|
||||
pk := tests.GetMakerTestKey(t)
|
||||
trustedForwarder := deployForwarder(t, ec, pk)
|
||||
err := checkForwarderContractCode(context.Background(), ec, trustedForwarder)
|
||||
err := CheckForwarderContractCode(context.Background(), ec, trustedForwarder)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
|
||||
2
go.mod
2
go.mod
@@ -9,7 +9,7 @@ require (
|
||||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
github.com/athanorlabs/go-dleq v0.0.0-20221228030413-8ac300febf46
|
||||
github.com/athanorlabs/go-p2p-net v0.0.0-20230109212235-d22ff2447282
|
||||
github.com/athanorlabs/go-relayer v0.0.4
|
||||
github.com/athanorlabs/go-relayer v0.0.5
|
||||
github.com/athanorlabs/go-relayer-client v0.0.0-20221103041240-2aad2e8fc742
|
||||
github.com/btcsuite/btcd/btcutil v1.1.2
|
||||
github.com/cockroachdb/apd/v3 v3.1.2
|
||||
|
||||
4
go.sum
4
go.sum
@@ -67,8 +67,8 @@ github.com/athanorlabs/go-dleq v0.0.0-20221228030413-8ac300febf46 h1:4N2dClTJzeo
|
||||
github.com/athanorlabs/go-dleq v0.0.0-20221228030413-8ac300febf46/go.mod h1:DWry6jSD7A13MKmeZA0AX3/xBeQCXDoygX99VPwL3yU=
|
||||
github.com/athanorlabs/go-p2p-net v0.0.0-20230109212235-d22ff2447282 h1:jDOEysh6BxVc+EWWEG02vtdKsZWlo15DB4i8JnjUt+8=
|
||||
github.com/athanorlabs/go-p2p-net v0.0.0-20230109212235-d22ff2447282/go.mod h1:U++jd4bNvI6qMDysd0gLWVst4DtHDEYvnoQAk4n7cx4=
|
||||
github.com/athanorlabs/go-relayer v0.0.4 h1:KcibOFqXZJ6D/BzYAUpvtlG2LNu5NiN1MaCLxVWb4Z4=
|
||||
github.com/athanorlabs/go-relayer v0.0.4/go.mod h1:7zbS8EWdZaMqfvi/mMxN+f0gqczwBHeOWhEu80d6/Uk=
|
||||
github.com/athanorlabs/go-relayer v0.0.5 h1:DRpEcwM5C5zmpa9HxLh0fWILySgrzN8bLA77rmd5teo=
|
||||
github.com/athanorlabs/go-relayer v0.0.5/go.mod h1:0Kbt5AKMMNBI6cdx3zB7+xon+NESveiJHFUlAfyxugc=
|
||||
github.com/athanorlabs/go-relayer-client v0.0.0-20221103041240-2aad2e8fc742 h1:8XIlVAZ5K40kQw8qpQW6fb9rbYr4yzyajhHkMw9rMV0=
|
||||
github.com/athanorlabs/go-relayer-client v0.0.0-20221103041240-2aad2e8fc742/go.mod h1:LzKxSadMjZ9ku9Nxt11AcVgoRzZtv8yE+tUsdszXKUo=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
|
||||
@@ -39,7 +39,6 @@ func runRelayer(
|
||||
ec *ethclient.Client,
|
||||
forwarderAddress ethcommon.Address,
|
||||
sk *ecdsa.PrivateKey,
|
||||
chainID *big.Int,
|
||||
) string {
|
||||
iforwarder, err := gsnforwarder.NewIForwarder(forwarderAddress, ec)
|
||||
require.NoError(t, err)
|
||||
@@ -48,12 +47,13 @@ func runRelayer(
|
||||
key := rcommon.NewKeyFromPrivateKey(sk)
|
||||
|
||||
cfg := &relayer.Config{
|
||||
Ctx: ctx,
|
||||
EthClient: ec,
|
||||
Forwarder: fw,
|
||||
Key: key,
|
||||
ChainID: chainID,
|
||||
NewForwardRequestFunc: gsnforwarder.NewIForwarderForwardRequest,
|
||||
Ctx: ctx,
|
||||
EthClient: ec,
|
||||
Forwarder: fw,
|
||||
Key: key,
|
||||
ValidateTransactionFunc: func(_ *rcommon.SubmitTransactionRequest) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
r, err := relayer.NewRelayer(cfg)
|
||||
@@ -152,7 +152,7 @@ func testSwapStateClaimRelayer(t *testing.T, sk *ecdsa.PrivateKey, asset types.E
|
||||
t.Logf("gas cost to call RegisterDomainSeparator: %d", receipt.GasUsed)
|
||||
|
||||
// start relayer
|
||||
relayerEndpoint := runRelayer(t, ctx, conn, forwarderAddress, relayerSk, chainID)
|
||||
relayerEndpoint := runRelayer(t, ctx, conn, forwarderAddress, relayerSk)
|
||||
|
||||
// deploy swap contract with claim key hash
|
||||
contractAddr, tx, contract, err := contracts.DeploySwapFactory(txOpts, conn, forwarderAddress)
|
||||
|
||||
@@ -20,8 +20,3 @@ if [[ -n "${ALL}" ]]; then
|
||||
else
|
||||
go install -tags=prod ./cmd/swapd ./cmd/swapcli
|
||||
fi
|
||||
|
||||
# Since we are inside a project using go modules when performing this
|
||||
# install, the version installed will match the go-relayer dependency in
|
||||
# our go.mod file. To override, add a @version suffix on the end.
|
||||
go install github.com/athanorlabs/go-relayer/cmd/relayer
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
PROJECT_ROOT="$(dirname "$(dirname "$(realpath "$0")")")"
|
||||
cd "${PROJECT_ROOT}" || exit 1
|
||||
|
||||
./scripts/build.sh || exit 1
|
||||
ALL=true ./scripts/build.sh || exit 1
|
||||
|
||||
source "scripts/testlib.sh"
|
||||
check-set-swap-test-data-dir
|
||||
@@ -46,8 +46,10 @@ start-relayer() {
|
||||
echo "Starting relayer with logs in ${log_file}"
|
||||
./bin/relayer \
|
||||
--deploy \
|
||||
--endpoint="http://localhost:${GANACHE_PORT}" \
|
||||
--data-dir="${SWAP_TEST_DATA_DIR}" \
|
||||
--ethereum-endpoint="http://localhost:${GANACHE_PORT}" \
|
||||
--log-level=debug \
|
||||
--rpc \
|
||||
--rpc-port="${RELAYER_PORT}" \
|
||||
--key="${SWAP_TEST_DATA_DIR}/relayer/eth.key" \
|
||||
&>"${log_file}" &
|
||||
|
||||
Reference in New Issue
Block a user