diff --git a/cmd/swapcli/main.go b/cmd/swapcli/main.go index b56b191cb..fd1c86871 100644 --- a/cmd/swapcli/main.go +++ b/cmd/swapcli/main.go @@ -169,11 +169,13 @@ func cliApp() *cli.App { Flags: []cli.Flag{ &cli.StringFlag{ Name: flagMinAmount, + Aliases: []string{"min"}, Usage: "Minimum amount to be swapped, in XMR", Required: true, }, &cli.StringFlag{ Name: flagMaxAmount, + Aliases: []string{"max"}, Usage: "Maximum amount to be swapped, in XMR", Required: true, }, @@ -184,15 +186,16 @@ func cliApp() *cli.App { }, &cli.BoolFlag{ Name: flagDetached, - Usage: "Exit immediately instead of subscribing to notifications about the swap's status", + Usage: "Exit immediately without subscribing to status notifications", }, &cli.StringFlag{ Name: flagToken, - Usage: "Use to pass the ethereum ERC20 token address to receive instead of ETH", + Usage: "Ethereum ERC20 token address to receive instead of ETH", }, &cli.BoolFlag{ - Name: flagUseRelayer, - Usage: "Use the relayer even if the receiving account has enough ETH to claim", + Name: flagUseRelayer, + Usage: "Use the relayer even if the receiving account has enough ETH to claim", + Hidden: true, // useful for testing, but no clear end-user use case for the flag }, swapdPortFlag, }, @@ -215,12 +218,13 @@ func cliApp() *cli.App { }, &cli.StringFlag{ Name: flagProvidesAmount, + Aliases: []string{"pa"}, Usage: "Amount of coin to send in the swap", Required: true, }, &cli.BoolFlag{ Name: flagDetached, - Usage: "Exit immediately instead of subscribing to notifications about the swap's status", + Usage: "Exit immediately without subscribing to status notifications", }, swapdPortFlag, }, @@ -251,7 +255,7 @@ func cliApp() *cli.App { }, { Name: "cancel", - Usage: "Cancel a ongoing swap if possible. Depending on the swap stage, this may not be possible.", + Usage: "Cancel ongoing swap, if possible at the current swap stage.", Action: runCancel, Flags: []cli.Flag{ &cli.StringFlag{ @@ -308,8 +312,8 @@ func cliApp() *cli.App { }, }, { - Name: "suggested-exchange-rate", - Aliases: []string{"price-feed"}, + Name: "price-feed", + Aliases: []string{"suggested-exchange-rate"}, Usage: "Returns the current mainnet exchange rate based on ETH/USD and XMR/USD price feeds.", Action: runSuggestedExchangeRate, Flags: []cli.Flag{swapdPortFlag}, diff --git a/common/consts.go b/common/consts.go index 3bd6b0b59..2c3d67867 100644 --- a/common/consts.go +++ b/common/consts.go @@ -28,8 +28,9 @@ const ( // Ethereum chain IDs const ( - MainnetChainID = 1 - SepoliaChainID = 11155111 - GanacheChainID = 1337 - HardhatChainID = 31337 + MainnetChainID = 1 + OpMainnetChainID = 10 // Optimism + SepoliaChainID = 11155111 + GanacheChainID = 1337 + HardhatChainID = 31337 ) diff --git a/pricefeed/pricefeed.go b/pricefeed/pricefeed.go index 16b1e3f46..b1c9a873c 100644 --- a/pricefeed/pricefeed.go +++ b/pricefeed/pricefeed.go @@ -21,17 +21,16 @@ import ( ) const ( - // mainnetEndpoint is a mainnet ethereum endpoint, from - // https://chainlist.org/chain/1, which stagenet users get pointed at for - // price feeds, as Sepolia doesn't have an XMR feed. Mainnet users will use - // the same ethereum endpoint that they use for other swap transactions. - mainnetEndpoint = "https://eth-rpc.gateway.pokt.network" + // optimismEndpoint is an RPC endpoint for optimism mainnet. Note that we + // tried https://mainnet.optimism.io first, but it is severely rate limited + // to around 2 requests/second. + optimismEndpoint = "https://1rpc.io/op" - // https://data.chain.link/ethereum/mainnet/crypto-usd/eth-usd - chainlinkETHToUSDProxy = "0x5f4ec3df9cbd43714fe2740f5e3616155c5b8419" + // https://data.chain.link/optimism/mainnet/crypto-usd/eth-usd + chainlinkETHToUSDProxy = "0x13e3ee699d1909e989722e753853ae30b17e08c5" - // https://data.chain.link/ethereum/mainnet/crypto-usd/xmr-usd - chainlinkXMRToUSDProxy = "0xfa66458cce7dd15d8650015c4fce4d278271618f" + // https://data.chain.link/optimism/mainnet/crypto-usd/xmr-usd + chainlinkXMRToUSDProxy = "0x2a8d91686a048e98e6ccf1a89e82f40d14312672" ) var ( @@ -47,7 +46,6 @@ type PriceFeed struct { } // GetETHUSDPrice returns the current ETH/USD price from the Chainlink oracle. -// It errors if the chain ID is not the Ethereum mainnet. func GetETHUSDPrice(ctx context.Context, ec *ethclient.Client) (*PriceFeed, error) { chainID, err := ec.ChainID(ctx) if err != nil { @@ -55,11 +53,10 @@ func GetETHUSDPrice(ctx context.Context, ec *ethclient.Client) (*PriceFeed, erro } switch chainID.Uint64() { - case common.MainnetChainID: + case common.OpMainnetChainID: // No extra work to do - case common.SepoliaChainID: - // Push stagenet/sepolia users to a mainnet endpoint - ec, err = ethclient.Dial(mainnetEndpoint) + case common.MainnetChainID, common.SepoliaChainID: + ec, err = ethclient.Dial(optimismEndpoint) if err != nil { return nil, err } @@ -78,25 +75,18 @@ func GetETHUSDPrice(ctx context.Context, ec *ethclient.Client) (*PriceFeed, erro } // GetXMRUSDPrice returns the current XMR/USD price from the Chainlink oracle. -// It errors if the chain ID is not the Ethereum mainnet. func GetXMRUSDPrice(ctx context.Context, ec *ethclient.Client) (*PriceFeed, error) { chainID, err := ec.ChainID(ctx) if err != nil { return nil, err } - // Temporary hack to return a better error until the issue is resolved. switch chainID.Uint64() { - case common.MainnetChainID, common.SepoliaChainID: - return nil, errors.New("https://github.com/AthanorLabs/atomic-swap/issues/492") - } - - switch chainID.Uint64() { - case common.MainnetChainID: + case common.OpMainnetChainID: // No extra work to do - case common.SepoliaChainID: + case common.MainnetChainID, common.SepoliaChainID: // Push stagenet/sepolia users to a mainnet endpoint - ec, err = ethclient.Dial(mainnetEndpoint) + ec, err = ethclient.Dial(optimismEndpoint) if err != nil { return nil, err } diff --git a/pricefeed/pricefeed_test.go b/pricefeed/pricefeed_test.go index f2ef7b63a..2ee67f88c 100644 --- a/pricefeed/pricefeed_test.go +++ b/pricefeed/pricefeed_test.go @@ -7,6 +7,7 @@ import ( "context" "testing" + "github.com/ethereum/go-ethereum/ethclient" logging "github.com/ipfs/go-log/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -15,18 +16,28 @@ import ( ) func init() { - logging.SetLogLevel("pricefeed", "debug") + _ = logging.SetLogLevel("pricefeed", "debug") } -func TestGetETHUSDPrice_mainnet(t *testing.T) { - ec := tests.NewEthMainnetClient(t) - - feed, err := GetETHUSDPrice(context.Background(), ec) +func newOptimismClient(t *testing.T) *ethclient.Client { + ec, err := ethclient.Dial(optimismEndpoint) require.NoError(t, err) - t.Logf("%s is $%s (updated: %s)", feed.Description, feed.Price, feed.UpdatedAt) - assert.Equal(t, "ETH / USD", feed.Description) - assert.False(t, feed.Price.Negative) - assert.False(t, feed.Price.IsZero()) + t.Cleanup(func() { + ec.Close() + }) + + return ec +} + +func TestGetETHUSDPrice_nonDev(t *testing.T) { + for _, ec := range []*ethclient.Client{tests.NewEthSepoliaClient(t), newOptimismClient(t)} { + feed, err := GetETHUSDPrice(context.Background(), ec) + require.NoError(t, err) + t.Logf("%s is $%s (updated: %s)", feed.Description, feed.Price, feed.UpdatedAt) + assert.Equal(t, "ETH / USD", feed.Description) + assert.False(t, feed.Price.Negative) + assert.False(t, feed.Price.IsZero()) + } } func TestGetETHUSDPrice_dev(t *testing.T) { @@ -37,16 +48,15 @@ func TestGetETHUSDPrice_dev(t *testing.T) { assert.Equal(t, "1234.12345678", feed.Price.String()) } -func TestGetXMRUSDPrice_mainnet(t *testing.T) { - t.Skip("Chainlink XMR price feed is down: https://github.com/AthanorLabs/atomic-swap/issues/492") - ec := tests.NewEthMainnetClient(t) - - feed, err := GetXMRUSDPrice(context.Background(), ec) - require.NoError(t, err) - t.Logf("%s is $%s (updated: %s)", feed.Description, feed.Price, feed.UpdatedAt) - assert.Equal(t, "XMR / USD", feed.Description) - assert.False(t, feed.Price.Negative) - assert.False(t, feed.Price.IsZero()) +func TestGetXMRUSDPrice_nonDev(t *testing.T) { + for _, ec := range []*ethclient.Client{tests.NewEthSepoliaClient(t), newOptimismClient(t)} { + feed, err := GetXMRUSDPrice(context.Background(), ec) + require.NoError(t, err) + t.Logf("%s is $%s (updated: %s)", feed.Description, feed.Price, feed.UpdatedAt) + assert.Equal(t, "XMR / USD", feed.Description) + assert.False(t, feed.Price.Negative) + assert.False(t, feed.Price.IsZero()) + } } func TestGetXMRUSDPrice_dev(t *testing.T) {