Files
atomic-swap/daemon/swap_daemon_erc20_test.go
2023-05-30 21:49:22 -05:00

119 lines
3.5 KiB
Go

package daemon
import (
"sync"
"testing"
"time"
"github.com/cockroachdb/apd/v3"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/athanorlabs/atomic-swap/coins"
"github.com/athanorlabs/atomic-swap/common/rpctypes"
"github.com/athanorlabs/atomic-swap/common/types"
"github.com/athanorlabs/atomic-swap/monero"
"github.com/athanorlabs/atomic-swap/rpcclient"
"github.com/athanorlabs/atomic-swap/tests"
)
// Tests the scenario where Bob has XMR and enough ETH to pay gas fees for the token claim. He
// exchanges 2 XMR for 3 of Alice's ERC20 tokens.
func TestRunSwapDaemon_ExchangesXMRForERC20Tokens(t *testing.T) {
minXMR := coins.StrToDecimal("1")
maxXMR := coins.StrToDecimal("2")
exRate := coins.StrToExchangeRate("1.5")
bobConf := CreateTestConf(t, tests.GetMakerTestKey(t))
monero.MineMinXMRBalance(t, bobConf.MoneroClient, coins.MoneroToPiconero(maxXMR))
aliceConf := CreateTestConf(t, tests.GetTakerTestKey(t))
tokenAsset := getMockTetherAsset(t, aliceConf.EthereumClient)
timeout := 7 * time.Minute
ctx, _ := LaunchDaemons(t, timeout, aliceConf, bobConf)
bc := rpcclient.NewClient(ctx, bobConf.RPCPort)
ac := rpcclient.NewClient(ctx, aliceConf.RPCPort)
bobStartTokenBal, err := bobConf.EthereumClient.ERC20Balance(ctx, tokenAsset.Address())
require.NoError(t, err)
_, bobStatusCh, err := bc.MakeOfferAndSubscribe(minXMR, maxXMR, exRate, tokenAsset, false)
require.NoError(t, err)
time.Sleep(250 * time.Millisecond) // offer propagation time
// Have Alice query all the offer information back
peersWithOffers, err := ac.QueryAll(coins.ProvidesXMR, 3)
require.NoError(t, err)
require.Len(t, peersWithOffers, 1)
require.Len(t, peersWithOffers[0].Offers, 1)
peerID := peersWithOffers[0].PeerID
offer := peersWithOffers[0].Offers[0]
tokenInfo, err := ac.TokenInfo(offer.EthAsset.Address())
require.NoError(t, err)
providesAmt, err := exRate.ToERC20Amount(offer.MaxAmount, tokenInfo)
require.NoError(t, err)
aliceStatusCh, err := ac.TakeOfferAndSubscribe(peerID, offer.ID, providesAmt)
require.NoError(t, err)
var statusWG sync.WaitGroup
statusWG.Add(2)
// Ensure Alice completes the swap successfully
go func() {
defer statusWG.Done()
for {
select {
case status := <-aliceStatusCh:
t.Log("> Alice got status:", status)
if !status.IsOngoing() {
assert.Equal(t, types.CompletedSuccess.String(), status.String())
return
}
case <-ctx.Done():
t.Errorf("Alice's context cancelled before she completed the swap")
return
}
}
}()
// Test that Bob completes the swap successfully
go func() {
defer statusWG.Done()
for {
select {
case status := <-bobStatusCh:
t.Log("> Bob got status:", status)
if !status.IsOngoing() {
assert.Equal(t, types.CompletedSuccess.String(), status.String())
return
}
case <-ctx.Done():
t.Errorf("Bob's context cancelled before he completed the swap")
return
}
}
}()
statusWG.Wait()
if t.Failed() {
return
}
//
// Check Bob's token balance via RPC method instead of doing it directly
//
endBalances, err := bc.Balances(&rpctypes.BalancesRequest{TokenAddrs: []ethcommon.Address{tokenAsset.Address()}})
require.NoError(t, err)
require.NotEmpty(t, endBalances.TokenBalances)
delta := new(apd.Decimal)
_, err = coins.DecimalCtx().Sub(delta, endBalances.TokenBalances[0].Amount, bobStartTokenBal.Amount)
require.NoError(t, err)
require.Equal(t, providesAmt.Text('f'), delta.Text('f'))
}