Skip to content

Commit

Permalink
client/mm: CEX balance tracking and auto-rebalancing (#2568)
Browse files Browse the repository at this point in the history
* multi: Return TxID from Send and add TransactionConfirmations

Send and Withdraw are updated to return a transaction ID. Also,
a TransactionConfirmations function is added to the wallet interface
which allows callers to determine the amount of confirmations a wallet
transaction has.

* client/mm/libxc: Add deposit/withdraw functionality to CEX interface

- Deposit and Withdraw functionality is added to the CEX interface
and is implemented in Binance.
- The CEX interface is updated to take uint32 assetIDs instead of strings
to represent assets.
- The "weth.polygon" and "wbtc.polygon" assets are renamed to be
"eth.polygon" and "btc.polygon". This makes it clear that "btc" and
"btc.polygon" are the same asset, just on different networks.

* client/mm: Tracking of CEX balances

Tracking of CEX balances for each bot is implemented in a similar fashion
to tracking bot balances on the DEX. A `wrappedCEX` is created for each
bot which behaves as if the entire balance of the CEX is the amount that
is allocated for each bot.

* client/mm: Simple arb bot auto-rebalancing

This implements auto-rebalancing in the simple arbitrage strategy. This is
an optional functionality that allows the user to specify a minimum
balance of each asset that should be on both the DEX and the CEX. If the
balance dips below this amount, then either a deposit or withdrawal will
be done to have an equal amount of the asset on both exchanges.
  • Loading branch information
martonp authored Dec 2, 2023
1 parent 79c70e9 commit f9289bd
Show file tree
Hide file tree
Showing 35 changed files with 5,059 additions and 982 deletions.
11 changes: 11 additions & 0 deletions client/asset/btc/btc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4339,6 +4339,17 @@ func (btc *baseWallet) SwapConfirmations(_ context.Context, id dex.Bytes, contra
return btc.node.swapConfirmations(txHash, vout, pkScript, startTime)
}

// TransactionConfirmations gets the number of confirmations for the specified
// transaction.
func (btc *baseWallet) TransactionConfirmations(ctx context.Context, txID string) (confs uint32, err error) {
txHash, err := chainhash.NewHashFromStr(txID)
if err != nil {
return 0, fmt.Errorf("error decoding txid %q: %w", txID, err)
}
_, confs, err = btc.rawWalletTx(txHash)
return
}

// RegFeeConfirmations gets the number of confirmations for the specified output
// by first checking for a unspent output, and if not found, searching indexed
// wallet transactions.
Expand Down
1 change: 1 addition & 0 deletions client/asset/btc/btc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2489,6 +2489,7 @@ func (c *tCoin) ID() dex.Bytes {
return make([]byte, 36)
}
func (c *tCoin) String() string { return hex.EncodeToString(c.id) }
func (c *tCoin) TxID() string { return hex.EncodeToString(c.id) }
func (c *tCoin) Value() uint64 { return 100 }

func TestReturnCoins(t *testing.T) {
Expand Down
5 changes: 5 additions & 0 deletions client/asset/btc/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ func (op *Output) String() string {
return op.Pt.String()
}

// TxID is the ID of the transaction used to create the coin.
func (op *Output) TxID() string {
return op.txHash().String()
}

// txHash returns the pointer of the wire.OutPoint's Hash.
func (op *Output) txHash() *chainhash.Hash {
return &op.Pt.TxHash
Expand Down
18 changes: 18 additions & 0 deletions client/asset/dcr/dcr.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,10 @@ func (op *output) ID() dex.Bytes {
return toCoinID(op.txHash(), op.vout())
}

func (op *output) TxID() string {
return op.txHash().String()
}

// String is a string representation of the coin.
func (op *output) String() string {
return op.pt.String()
Expand Down Expand Up @@ -4353,6 +4357,20 @@ func (dcr *ExchangeWallet) SwapConfirmations(ctx context.Context, coinID, contra
return confs, spent, err
}

// TransactionConfirmations gets the number of confirmations for the specified
// transaction.
func (dcr *ExchangeWallet) TransactionConfirmations(ctx context.Context, txID string) (confs uint32, err error) {
txHash, err := chainhash.NewHashFromStr(txID)
if err != nil {
return 0, fmt.Errorf("error decoding txid %s: %w", txID, err)
}
tx, err := dcr.wallet.GetTransaction(ctx, txHash)
if err != nil {
return 0, err
}
return uint32(tx.Confirmations), nil
}

// RegFeeConfirmations gets the number of confirmations for the specified
// output.
func (dcr *ExchangeWallet) RegFeeConfirmations(ctx context.Context, coinID dex.Bytes) (confs uint32, err error) {
Expand Down
1 change: 1 addition & 0 deletions client/asset/dcr/dcr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,7 @@ func (c *tCoin) ID() dex.Bytes {
func (c *tCoin) String() string { return hex.EncodeToString(c.id) }
func (c *tCoin) Value() uint64 { return 100 }
func (c *tCoin) Confirmations(ctx context.Context) (uint32, error) { return 2, nil }
func (c *tCoin) TxID() string { return hex.EncodeToString(c.id) }

func TestReturnCoins(t *testing.T) {
wallet, node, shutdown := tNewWallet()
Expand Down
11 changes: 11 additions & 0 deletions client/asset/eth/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -1408,6 +1408,10 @@ func (c *coin) ID() dex.Bytes {
return c.id[:]
}

func (c *coin) TxID() string {
return c.String()
}

// String is a string representation of the coin.
func (c *coin) String() string {
return c.id.String()
Expand Down Expand Up @@ -3346,6 +3350,13 @@ func (eth *baseWallet) swapOrRedemptionFeesPaid(ctx context.Context, coinID, con
return dexeth.WeiToGwei(bigFees), secretHashes, nil
}

// TransactionConfirmations gets the number of confirmations for the specified
// transaction.
func (eth *baseWallet) TransactionConfirmations(ctx context.Context, txID string) (confs uint32, err error) {
txHash := common.HexToHash(txID)
return eth.node.transactionConfirmations(ctx, txHash)
}

// RegFeeConfirmations gets the number of confirmations for the specified
// transaction.
func (eth *baseWallet) RegFeeConfirmations(ctx context.Context, coinID dex.Bytes) (confs uint32, err error) {
Expand Down
3 changes: 3 additions & 0 deletions client/asset/eth/eth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1483,6 +1483,9 @@ func (*badCoin) ID() dex.Bytes {
func (*badCoin) String() string {
return "abc"
}
func (*badCoin) TxID() string {
return "abc"
}
func (b *badCoin) Value() uint64 {
return uint64(*b)
}
Expand Down
8 changes: 8 additions & 0 deletions client/asset/eth/fundingcoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ func (c *fundingCoin) ID() dex.Bytes {
return []byte(c.addr.String())
}

func (c *fundingCoin) TxID() string {
return ""
}

// Value returns the value reserved in the funding coin.
func (c *fundingCoin) Value() uint64 {
return c.amt
Expand Down Expand Up @@ -89,6 +93,10 @@ func (c *tokenFundingCoin) ID() dex.Bytes {
return []byte(c.addr.String())
}

func (c *tokenFundingCoin) TxID() string {
return ""
}

// ID creates a byte slice that can be decoded with DecodeCoinID.
func (c *tokenFundingCoin) RecoveryID() dex.Bytes {
b := make([]byte, tokenFundingCoinIDSize)
Expand Down
6 changes: 6 additions & 0 deletions client/asset/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,10 @@ type Wallet interface {
// payment. This method need not be supported by all assets. Those assets
// which do no support DEX registration fees will return an ErrUnsupported.
RegFeeConfirmations(ctx context.Context, coinID dex.Bytes) (confs uint32, err error)
// TransactionConfirmations gets the number of confirmations for the
// specified transaction. If the wallet does not know about the
// transaction, asset.CoinNotFoundError is returned.
TransactionConfirmations(ctx context.Context, txID string) (confs uint32, err error)
// Send sends the exact value to the specified address. This is different
// from Withdraw, which subtracts the tx fees from the amount sent.
Send(address string, value, feeRate uint64) (Coin, error)
Expand Down Expand Up @@ -1217,6 +1221,8 @@ type Coin interface {
String() string
// Value is the available quantity, in atoms/satoshi.
Value() uint64
// TxID is the ID of the transaction that created the coin.
TxID() string
}

type RecoveryCoin interface {
Expand Down
15 changes: 15 additions & 0 deletions client/asset/zec/zec.go
Original file line number Diff line number Diff line change
Expand Up @@ -2123,6 +2123,21 @@ func (w *zecWallet) Send(address string, value, feeRate uint64) (asset.Coin, err
return btc.NewOutput(txHash, vout, sent), nil
}

// TransactionConfirmations gets the number of confirmations for the specified
// transaction.
func (w *zecWallet) TransactionConfirmations(ctx context.Context, txID string) (confs uint32, err error) {
txHash, err := chainhash.NewHashFromStr(txID)
if err != nil {
return 0, fmt.Errorf("error decoding txid %q: %w", txID, err)
}
res, err := getWalletTransaction(w, txHash)
if err != nil {
return 0, err
}

return uint32(res.Confirmations), nil
}

// send the value to the address, with the given fee rate. If subtract is true,
// the fees will be subtracted from the value. If false, the fees are in
// addition to the value. feeRate is in units of sats/byte.
Expand Down
Loading

0 comments on commit f9289bd

Please sign in to comment.