decred.org/dcrdex@v1.0.3/client/asset/interface.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package asset
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  
    10  	"decred.org/dcrdex/dex"
    11  	"decred.org/dcrdex/dex/utils"
    12  	dcrwalletjson "decred.org/dcrwallet/v4/rpc/jsonrpc/types"
    13  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    14  )
    15  
    16  // WalletTrait is a bitset indicating various optional wallet features, such as
    17  // the presence of auxiliary methods like Rescan, Withdraw and Sweep.
    18  type WalletTrait uint64
    19  
    20  const (
    21  	WalletTraitRescanner      WalletTrait = 1 << iota // The Wallet is an asset.Rescanner.
    22  	WalletTraitNewAddresser                           // The Wallet can generate new addresses on demand with NewAddress.
    23  	WalletTraitLogFiler                               // The Wallet allows for downloading of a log file.
    24  	WalletTraitFeeRater                               // Wallet can provide a fee rate for non-critical transactions
    25  	WalletTraitAccelerator                            // This wallet can accelerate transactions using the CPFP technique
    26  	WalletTraitRecoverer                              // The wallet is an asset.Recoverer.
    27  	WalletTraitWithdrawer                             // The Wallet can withdraw a specific amount from an exchange wallet.
    28  	WalletTraitSweeper                                // The Wallet can sweep all the funds, leaving no change.
    29  	WalletTraitRestorer                               // The wallet is an asset.WalletRestorer
    30  	WalletTraitTxFeeEstimator                         // The wallet can estimate transaction fees.
    31  	WalletTraitPeerManager                            // The wallet can manage its peers.
    32  	WalletTraitAuthenticator                          // The wallet require authentication.
    33  	WalletTraitShielded                               // DEPRECATED. Left for ordering
    34  	WalletTraitTokenApprover                          // The wallet is a TokenApprover
    35  	WalletTraitAccountLocker                          // The wallet must have enough balance for redemptions before a trade.
    36  	WalletTraitTicketBuyer                            // The wallet can participate in decred staking.
    37  	WalletTraitHistorian                              // This wallet can return its transaction history
    38  	WalletTraitFundsMixer                             // The wallet can mix funds.
    39  	WalletTraitDynamicSwapper                         // The wallet has dynamic fees.
    40  )
    41  
    42  // IsRescanner tests if the WalletTrait has the WalletTraitRescanner bit set.
    43  func (wt WalletTrait) IsRescanner() bool {
    44  	return wt&WalletTraitRescanner != 0
    45  }
    46  
    47  // IsNewAddresser tests if the WalletTrait has the WalletTraitNewAddresser bit
    48  // set, which indicates the presence of a NewAddress method that will generate a
    49  // new address on each call. If this method does not exist, the Address method
    50  // should be assumed to always return the same receiving address.
    51  func (wt WalletTrait) IsNewAddresser() bool {
    52  	return wt&WalletTraitNewAddresser != 0
    53  }
    54  
    55  // IsLogFiler tests if WalletTrait has the WalletTraitLogFiler bit set.
    56  func (wt WalletTrait) IsLogFiler() bool {
    57  	return wt&WalletTraitLogFiler != 0
    58  }
    59  
    60  // IsFeeRater tests if the WalletTrait has the WalletTraitFeeRater bit set,
    61  // which indicates the presence of a FeeRate method.
    62  func (wt WalletTrait) IsFeeRater() bool {
    63  	return wt&WalletTraitFeeRater != 0
    64  }
    65  
    66  // IsAccelerator tests if the WalletTrait has the WalletTraitAccelerator bit set,
    67  // which indicates the presence of an Accelerate method.
    68  func (wt WalletTrait) IsAccelerator() bool {
    69  	return wt&WalletTraitAccelerator != 0
    70  }
    71  
    72  // IsRecoverer tests if the WalletTrait has the WalletTraitRecoverer bit set,
    73  // which indicates the wallet implements the Recoverer interface.
    74  func (wt WalletTrait) IsRecoverer() bool {
    75  	return wt&WalletTraitRecoverer != 0
    76  }
    77  
    78  // IsWithdrawer tests if the WalletTrait has the WalletTraitSender bit set,
    79  // which indicates the presence of a Withdraw method.
    80  func (wt WalletTrait) IsWithdrawer() bool {
    81  	return wt&WalletTraitWithdrawer != 0
    82  }
    83  
    84  // IsSweeper tests if the WalletTrait has the WalletTraitSweeper bit set, which
    85  // indicates the presence of a Sweep method.
    86  func (wt WalletTrait) IsSweeper() bool {
    87  	return wt&WalletTraitSweeper != 0
    88  }
    89  
    90  // IsRestorer tests if the WalletTrait has the WalletTraitRestorer bit set, which
    91  // indicates the wallet implements the WalletRestorer interface.
    92  func (wt WalletTrait) IsRestorer() bool {
    93  	return wt&WalletTraitRestorer != 0
    94  }
    95  
    96  // IsTxFeeEstimator tests if the WalletTrait has the WalletTraitTxFeeEstimator
    97  // bit set, which indicates the wallet implements the TxFeeEstimator interface.
    98  func (wt WalletTrait) IsTxFeeEstimator() bool {
    99  	return wt&WalletTraitTxFeeEstimator != 0
   100  }
   101  
   102  // IsPeerManager tests if the WalletTrait has the WalletTraitPeerManager bit
   103  // set, which indicates the wallet implements the PeerManager interface.
   104  func (wt WalletTrait) IsPeerManager() bool {
   105  	return wt&WalletTraitPeerManager != 0
   106  }
   107  
   108  // IsAuthenticator tests if WalletTrait has WalletTraitAuthenticator bit set,
   109  // which indicates authentication is required by wallet.
   110  func (wt WalletTrait) IsAuthenticator() bool {
   111  	return wt&WalletTraitAuthenticator != 0
   112  }
   113  
   114  // IsTokenApprover tests if the WalletTrait has the WalletTraitTokenApprover bit
   115  // set, which indicates the wallet implements the TokenApprover interface.
   116  func (wt WalletTrait) IsTokenApprover() bool {
   117  	return wt&WalletTraitTokenApprover != 0
   118  }
   119  
   120  // IsAccountLocker test if WalletTrait has WalletTraitAccountLocker bit set,
   121  // which indicates the wallet implements the AccountLocker interface.
   122  func (wt WalletTrait) IsAccountLocker() bool {
   123  	return wt&WalletTraitAccountLocker != 0
   124  }
   125  
   126  // IsTicketBuyer tests if the WalletTrait has the WalletTraitTicketBuyer bit set,
   127  // which indicates the wallet implements the TicketBuyer interface.
   128  func (wt WalletTrait) IsTicketBuyer() bool {
   129  	return wt&WalletTraitTicketBuyer != 0
   130  }
   131  
   132  // IsHistorian tests if the WalletTrait has the WalletTraitHistorian bit set,
   133  // which indicates the wallet implements the WalletHistorian interface.
   134  func (wt WalletTrait) IsHistorian() bool {
   135  	return wt&WalletTraitHistorian != 0
   136  }
   137  
   138  // IsFundsMixer tests if the WalletTrait has the WalletTraitFundsMixer bit set,
   139  // which indicates the wallet implements the FundsMixer interface.
   140  func (wt WalletTrait) IsFundsMixer() bool {
   141  	return wt&WalletTraitFundsMixer != 0
   142  }
   143  
   144  // IsDynamicSwapper tests if a wallet has the WalletTraitDynamicSwapper bit set,
   145  // which indicates the wallet implements the DynamicSwapper interface. This also
   146  // indicates that the wallet will initially report a higher estimated fee for
   147  // transactions then when a transaction is confirmed, the actual fee will be
   148  // known.
   149  func (wt WalletTrait) IsDynamicSwapper() bool {
   150  	return wt&WalletTraitDynamicSwapper != 0
   151  }
   152  
   153  // DetermineWalletTraits returns the WalletTrait bitset for the provided Wallet.
   154  func DetermineWalletTraits(w Wallet) (t WalletTrait) {
   155  	if _, is := w.(Rescanner); is {
   156  		t |= WalletTraitRescanner
   157  	}
   158  	if _, is := w.(NewAddresser); is {
   159  		t |= WalletTraitNewAddresser
   160  	}
   161  	if _, is := w.(LogFiler); is {
   162  		t |= WalletTraitLogFiler
   163  	}
   164  	if _, is := w.(FeeRater); is {
   165  		t |= WalletTraitFeeRater
   166  	}
   167  	if _, is := w.(Accelerator); is {
   168  		t |= WalletTraitAccelerator
   169  	}
   170  	if _, is := w.(Recoverer); is {
   171  		t |= WalletTraitRecoverer
   172  	}
   173  	if _, is := w.(Withdrawer); is {
   174  		t |= WalletTraitWithdrawer
   175  	}
   176  	if _, is := w.(Sweeper); is {
   177  		t |= WalletTraitSweeper
   178  	}
   179  	if _, is := w.(WalletRestorer); is {
   180  		t |= WalletTraitRestorer
   181  	}
   182  	if _, is := w.(TxFeeEstimator); is {
   183  		t |= WalletTraitTxFeeEstimator
   184  	}
   185  	if _, is := w.(PeerManager); is {
   186  		t |= WalletTraitPeerManager
   187  	}
   188  	if _, is := w.(Authenticator); is {
   189  		t |= WalletTraitAuthenticator
   190  	}
   191  	if _, is := w.(TokenApprover); is {
   192  		t |= WalletTraitTokenApprover
   193  	}
   194  	if _, is := w.(AccountLocker); is {
   195  		t |= WalletTraitAccountLocker
   196  	}
   197  	if _, is := w.(TicketBuyer); is {
   198  		t |= WalletTraitTicketBuyer
   199  	}
   200  	if _, is := w.(WalletHistorian); is {
   201  		t |= WalletTraitHistorian
   202  	}
   203  	if _, is := w.(FundsMixer); is {
   204  		t |= WalletTraitFundsMixer
   205  	}
   206  	if _, is := w.(DynamicSwapper); is {
   207  		t |= WalletTraitDynamicSwapper
   208  	}
   209  	return t
   210  }
   211  
   212  // CoinNotFoundError is returned when a coin cannot be found, either because it
   213  // has been spent or it never existed. This error may be returned from
   214  // AuditContract, Refund or Redeem as those methods expect the provided coin to
   215  // exist and be unspent.
   216  const (
   217  	// ErrSwapNotInitiated most likely means that a swap using a contract has
   218  	// not yet been mined. There is no guarantee that the swap will be mined
   219  	// in the future.
   220  	ErrSwapNotInitiated = dex.ErrorKind("swap not yet initiated")
   221  
   222  	CoinNotFoundError = dex.ErrorKind("coin not found")
   223  	// ErrTxRejected is returned when a transaction was rejected. This
   224  	// generally would indicate either an internal wallet error, or potentially
   225  	// a user using multiple instances of the wallet simultaneously. As such
   226  	// it may or may not be advisable to try the tx again without seeking
   227  	// further investigation.
   228  	ErrTxRejected = dex.ErrorKind("transaction was rejected")
   229  	// ErrTxLost is returned when the tx is irreparably lost, as would be the
   230  	// case if it's inputs were spent by another tx first or if it is not the
   231  	// accepted tx for a given nonce. These txs have incurred no fee losses, so
   232  	// the caller should feel free to re-issue the tx.
   233  	ErrTxLost         = dex.ErrorKind("tx lost")
   234  	ErrRequestTimeout = dex.ErrorKind("request timeout")
   235  	ErrConnectionDown = dex.ErrorKind("wallet not connected")
   236  	ErrNotImplemented = dex.ErrorKind("not implemented")
   237  	ErrUnsupported    = dex.ErrorKind("unsupported")
   238  	// ErrSwapRefunded is returned from ConfirmRedemption when the swap has
   239  	// been refunded before the user could redeem.
   240  	ErrSwapRefunded = dex.ErrorKind("swap refunded")
   241  	// ErrNotEnoughConfirms is returned when a transaction is confirmed,
   242  	// but does not have enough confirmations to be trusted.
   243  	ErrNotEnoughConfirms = dex.ErrorKind("transaction does not have enough confirmations")
   244  	// ErrWalletTypeDisabled indicates that a wallet type is no longer
   245  	// available.
   246  	ErrWalletTypeDisabled = dex.ErrorKind("wallet type has been disabled")
   247  	// ErrInsufficientBalance is returned when there is insufficient available
   248  	// balance for an operation, such as reserving funds for future bonds.
   249  	ErrInsufficientBalance = dex.ErrorKind("insufficient available balance")
   250  	// ErrIncorrectBondKey is returned when a provided private key is incorrect
   251  	// for a bond output.
   252  	ErrIncorrectBondKey = dex.ErrorKind("incorrect private key")
   253  	// ErrUnapprovedToken is returned when trying to fund an order using a token
   254  	// that has not been approved.
   255  	ErrUnapprovedToken = dex.ErrorKind("token not approved")
   256  	ErrApprovalPending = dex.ErrorKind("approval pending")
   257  
   258  	// InternalNodeLoggerName is the name for a logger that is used to fine
   259  	// tune log levels for only loggers using this name.
   260  	InternalNodeLoggerName = "INTL"
   261  
   262  	// VersionNewest can be passed to some estimation methods to instruct the
   263  	// wallet to use whatever is the newest version available.
   264  	VersionNewest = ^uint32(0)
   265  )
   266  
   267  type WalletDefinition struct {
   268  	// If seeded is true, the Create method will be called with a deterministic
   269  	// seed that should be used to set the wallet key(s). This would be
   270  	// true for built-in wallets.
   271  	Seeded bool `json:"seeded"`
   272  	// Type is a string identifying the wallet type. NOTE: There should be a
   273  	// particular WalletTrait set for any given Type, but wallet construction is
   274  	// presently required to discern traits.
   275  	Type string `json:"type"`
   276  	// Tab is a displayable string for the wallet type. One or two words. First
   277  	// word capitalized. Displayed on a wallet selection tab.
   278  	Tab string `json:"tab"`
   279  	// Description is a short description of the wallet, suitable for a tooltip.
   280  	Description string `json:"description"`
   281  	// DefaultConfigPath is the default file path that the Wallet uses for its
   282  	// configuration file. Probably only useful for unseeded / external wallets.
   283  	DefaultConfigPath string `json:"configpath"`
   284  	// ConfigOpts is a slice of expected Wallet config options, with the display
   285  	// name, config key (for parsing the option from a config file/text) and
   286  	// description for each option. This can be used to request config info from
   287  	// users e.g. via dynamically generated GUI forms.
   288  	ConfigOpts []*ConfigOption `json:"configopts"`
   289  	// MultiFundingOpts are options related to funding multi-trades.
   290  	MultiFundingOpts []*OrderOption `json:"multifundingopts"`
   291  	// NoAuth indicates that the wallet does not implement the Authenticator
   292  	// interface. A better way to check is to use the wallet traits but wallet
   293  	// construction is presently required to discern traits.
   294  	NoAuth bool `json:"noauth"`
   295  	// GuideLink is a link to wallet configuration docs that the user may follow
   296  	// when creating a new wallet or updating its settings.
   297  	GuideLink string `json:"guidelink"`
   298  }
   299  
   300  // Token combines the generic dex.Token with a WalletDefinition.
   301  type Token struct {
   302  	*dex.Token
   303  	Definition      *WalletDefinition `json:"definition"`
   304  	ContractAddress string            `json:"contractAddress"` // Set in SetNetwork
   305  }
   306  
   307  // WalletInfo is auxiliary information about an ExchangeWallet.
   308  type WalletInfo struct {
   309  	// Name is the display name for the currency, e.g. "Decred"
   310  	Name string `json:"name"`
   311  	// SupportedVersions lists all supported asset versions. Several wallet
   312  	// methods accept a version argument to indicate which contract to use,
   313  	// however, the consumer (e.g. Core) is responsible for ensuring the
   314  	// server's asset version is supported before using the Wallet.
   315  	SupportedVersions []uint32 `json:"versions"`
   316  	// AvailableWallets is an ordered list of available WalletDefinition. The
   317  	// first WalletDefinition is considered the default, and might, for instance
   318  	// be the initial form offered to the user for configuration, with others
   319  	// available to select.
   320  	AvailableWallets []*WalletDefinition `json:"availablewallets"`
   321  	// LegacyWalletIndex should be set for assets that existed before wallets
   322  	// were typed. The index should point to the WalletDefinition that should
   323  	// be assumed when the type is provided as an empty string.
   324  	LegacyWalletIndex int `json:"emptyidx"`
   325  	// UnitInfo is the information about unit names and conversion factors for
   326  	// the asset.
   327  	UnitInfo dex.UnitInfo `json:"unitinfo"`
   328  	// MaxSwapsInTx is the max amount of swaps that this wallet can do in a
   329  	// single transaction.
   330  	MaxSwapsInTx uint64
   331  	// MaxRedeemsInTx is the max amount of redemptions that this wallet can do
   332  	// in a single transaction.
   333  	MaxRedeemsInTx uint64
   334  	// IsAccountBased should be set to true for account-based (EVM) assets, so
   335  	// that a common seed will be generated and wallets will generate the
   336  	// same address.
   337  	IsAccountBased bool
   338  }
   339  
   340  // ConfigOption is a wallet configuration option.
   341  type ConfigOption struct {
   342  	Key          string `json:"key"`
   343  	DisplayName  string `json:"displayname"`
   344  	Description  string `json:"description"`
   345  	DefaultValue any    `json:"default"`
   346  	// If MaxValue/MinValue are set to the string "now" for a date config, the
   347  	// UI will display the current date.
   348  	MaxValue          any `json:"max"`
   349  	MinValue          any `json:"min"`
   350  	Options           map[string]*ConfigOption
   351  	NoEcho            bool `json:"noecho"`
   352  	IsBoolean         bool `json:"isboolean"`
   353  	IsDate            bool `json:"isdate"`
   354  	DisableWhenActive bool `json:"disablewhenactive"`
   355  	// Repeatable signals a text input that can be duplicated and submitted
   356  	// multiple times, with the specified delimiter used to encode the data
   357  	// in the settings map.
   358  	Repeatable string `json:"repeatable"`
   359  	// RepeatN signals how many times text input should be repeated, replicating
   360  	// this option N times.
   361  	RepeatN  int32 `json:"repeatN"`
   362  	Required bool  `json:"required"`
   363  	// DependsOn is the key of another config option that if is set to true,
   364  	// this config option will be shown.
   365  	DependsOn string `json:"dependsOn"`
   366  
   367  	// ShowByDefault to show or not options on "hide advanced options".
   368  	ShowByDefault bool `json:"showByDefault,omitempty"`
   369  }
   370  
   371  const (
   372  	// SpecialSettingActivelyUsed is a special setting that can be injected by
   373  	// core that lets the wallet know that it is being actively used. A use
   374  	// case is by the bitcoin SPV wallet to decide whether or not it is safe
   375  	// to do a full rescan.
   376  	SpecialSettingActivelyUsed = "special_activelyUsed"
   377  )
   378  
   379  // WalletConfig is the configuration settings for the wallet. WalletConfig
   380  // is passed to the wallet constructor.
   381  type WalletConfig struct {
   382  	// Type is the type of wallet, corresponding to the Type field of an
   383  	// available WalletDefinition.
   384  	Type string
   385  	// Settings is the key-value store of wallet connection parameters. The
   386  	// Settings are supplied by the user according the the WalletInfo's
   387  	// ConfigOpts.
   388  	Settings map[string]string
   389  	// Emit is a WalletEmitter that manages a channel over which an asset may
   390  	// issue notifications. Every asset is expected to issue a TipChangeNote
   391  	// when a network change occurs that might require balance updates or action
   392  	// on swaps.
   393  	Emit *WalletEmitter
   394  	// PeersChange is a function that will be called when the number of
   395  	// wallet/node peers changes, or the wallet fails to get the count. This
   396  	// should not be called prior to Connect of the constructed wallet.
   397  	PeersChange func(uint32, error)
   398  	// DataDir is a filesystem directory the wallet may use for persistent
   399  	// storage.
   400  	DataDir string
   401  }
   402  
   403  // ConfirmRedemptionStatus contains the coinID which redeemed a swap, the
   404  // number of confirmations the transaction has, and the number of confirmations
   405  // required for it to be considered confirmed.
   406  type ConfirmRedemptionStatus struct {
   407  	Confs  uint64
   408  	Req    uint64
   409  	CoinID dex.Bytes
   410  }
   411  
   412  // Wallet is a common interface to be implemented by cryptocurrency wallet
   413  // software.
   414  type Wallet interface {
   415  	// It should be assumed that once disconnected, subsequent Connect calls
   416  	// will fail, requiring a new Wallet instance.
   417  	dex.Connector
   418  	// Info returns a set of basic information about the wallet driver.
   419  	Info() *WalletInfo
   420  	// Balance should return the balance of the wallet, categorized by
   421  	// available, immature, and locked. Balance takes a list of minimum
   422  	// confirmations for which to calculate maturity, and returns a list of
   423  	// corresponding *Balance.
   424  	Balance() (*Balance, error)
   425  	// FundOrder selects coins for use in an order. The coins will be locked,
   426  	// and will not be returned in subsequent calls to FundOrder or calculated
   427  	// in calls to Available, unless they are unlocked with ReturnCoins. The
   428  	// returned []dex.Bytes contains the redeem scripts for the selected coins.
   429  	// Equal number of coins and redeemed scripts must be returned. A nil or
   430  	// empty dex.Bytes should be appended to the redeem scripts collection for
   431  	// coins with no redeem script. The fees returned are any fees paid in the
   432  	// process of funding the order, such as transaction fees for a split tx.
   433  	FundOrder(*Order) (coins Coins, redeemScripts []dex.Bytes, fees uint64, err error)
   434  	// MaxOrder generates information about the maximum order size and
   435  	// associated fees that the wallet can support for the specified DEX. The
   436  	// fees are an estimate based on current network conditions, and will be <=
   437  	// the fees associated with the Asset.MaxFeeRate. For quote assets, lotSize
   438  	// will be an estimate based on current market conditions. lotSize should
   439  	// not be zero.
   440  	MaxOrder(*MaxOrderForm) (*SwapEstimate, error)
   441  	// PreSwap gets a pre-swap estimate for the specified order size.
   442  	PreSwap(*PreSwapForm) (*PreSwap, error)
   443  	// PreRedeem gets a pre-redeem estimate for the specified order size.
   444  	PreRedeem(*PreRedeemForm) (*PreRedeem, error)
   445  	// ReturnCoins unlocks coins. This would be necessary in the case of a
   446  	// canceled order. A nil Coins slice indicates to unlock all coins that the
   447  	// wallet may have locked, a syntax that should always be followed by
   448  	// FundingCoins for any active orders, and is thus only appropriate at time
   449  	// of login. Unlocking all coins is likely only useful for external wallets
   450  	// whose lifetime is longer than the asset.Wallet instance.
   451  	ReturnCoins(Coins) error
   452  	// FundingCoins gets funding coins for the coin IDs. The coins are locked.
   453  	// This method might be called to reinitialize an order from data stored
   454  	// externally. This method will only return funding coins, e.g. unspent
   455  	// transaction outputs.
   456  	FundingCoins([]dex.Bytes) (Coins, error)
   457  	// Swap sends the swaps in a single transaction. The Receipts returned can
   458  	// be used to refund a failed transaction. The Input coins are unlocked
   459  	// where necessary to ensure accurate balance reporting in cases where the
   460  	// wallet includes spent coins as part of the locked balance just because
   461  	// they were previously locked.
   462  	Swap(*Swaps) (receipts []Receipt, changeCoin Coin, feesPaid uint64, err error)
   463  	// Redeem sends the redemption transaction, which may contain more than one
   464  	// redemption. The input coin IDs and the output Coin are returned.
   465  	Redeem(redeems *RedeemForm) (ins []dex.Bytes, out Coin, feesPaid uint64, err error)
   466  	// SignMessage signs the coin ID with the private key associated with the
   467  	// specified Coin. A slice of pubkeys required to spend the Coin and a
   468  	// signature for each pubkey are returned.
   469  	SignMessage(Coin, dex.Bytes) (pubkeys, sigs []dex.Bytes, err error)
   470  	// AuditContract retrieves information about a swap contract from the
   471  	// provided txData and broadcasts the txData to ensure the contract is
   472  	// propagated to the blockchain. The information returned would be used
   473  	// to verify the counter-party's contract during a swap. It is not an
   474  	// error if the provided txData cannot be broadcasted because it may
   475  	// already be broadcasted. A successful audit response does not mean
   476  	// the tx exists on the blockchain, use SwapConfirmations to ensure
   477  	// the tx is mined.
   478  	AuditContract(coinID, contract, txData dex.Bytes, rebroadcast bool) (*AuditInfo, error)
   479  	// ContractLockTimeExpired returns true if the specified contract's locktime
   480  	// has expired, making it possible to issue a Refund. The contract expiry
   481  	// time is also returned, but reaching this time does not necessarily mean
   482  	// the contract can be refunded since assets have different rules to satisfy
   483  	// the lock. For example, in Bitcoin the median of the last 11 blocks must
   484  	// be past the expiry time, not the current time.
   485  	ContractLockTimeExpired(ctx context.Context, contract dex.Bytes) (bool, time.Time, error)
   486  	// FindRedemption watches for the input that spends the specified
   487  	// coin and contract, and returns the spending input and the
   488  	// secret key when it finds a spender.
   489  	//
   490  	// For typical utxo-based blockchains, every input of every block tx
   491  	// (starting at the contract block) will need to be scanned until a spending
   492  	// input is found.
   493  	//
   494  	// FindRedemption is necessary to deal with the case of a maker redeeming
   495  	// but not forwarding their redemption information. The DEX does not monitor
   496  	// for this case. While it will result in the counter-party being penalized,
   497  	// the input still needs to be found so the swap can be completed.
   498  	//
   499  	// NOTE: This could potentially be a long and expensive operation if
   500  	// performed long after the swap is broadcast; might be better executed from
   501  	// a goroutine.
   502  	FindRedemption(ctx context.Context, coinID, contract dex.Bytes) (redemptionCoin, secret dex.Bytes, err error)
   503  	// Refund refunds a contract. This can only be used after the time lock has
   504  	// expired AND if the contract has not been redeemed/refunded. This method
   505  	// MUST return an asset.CoinNotFoundError error if the swap is already
   506  	// spent, which is used to indicate if FindRedemption should be used and the
   507  	// counterparty's swap redeemed. NOTE: The contract cannot be retrieved from
   508  	// the unspent coin info as the wallet does not store it, even though it was
   509  	// known when the init transaction was created. The client should store this
   510  	// information for persistence across sessions.
   511  	Refund(coinID, contract dex.Bytes, feeRate uint64) (dex.Bytes, error)
   512  	// DepositAddress returns an address for depositing funds into Bison Wallet.
   513  	DepositAddress() (string, error)
   514  	// OwnsDepositAddress indicates if the provided address can be used
   515  	// to deposit funds into the wallet.
   516  	OwnsDepositAddress(address string) (bool, error)
   517  	// RedemptionAddress gets an address for use in redeeming the counterparty's
   518  	// swap. This would be included in their swap initialization. This is
   519  	// presently called when each trade order is *submitted*. If these are
   520  	// unique addresses and the orders are canceled, the gap limit may become a
   521  	// hurdle when restoring the wallet.
   522  	RedemptionAddress() (string, error)
   523  	// LockTimeExpired returns true if the specified locktime has expired,
   524  	// making it possible to redeem the locked coins.
   525  	LockTimeExpired(ctx context.Context, lockTime time.Time) (bool, error)
   526  	// SwapConfirmations gets the number of confirmations and the spend status
   527  	// for the specified swap. If the swap was not funded by this wallet, and
   528  	// it is already spent, you may see CoinNotFoundError.
   529  	// If the coin is located, but recognized as spent, no error is returned.
   530  	// If the contract is already redeemed or refunded, the confs value may not
   531  	// be accurate.
   532  	// The contract and matchTime are provided so that wallets may search for
   533  	// the coin using light filters.
   534  	SwapConfirmations(ctx context.Context, coinID dex.Bytes, contract dex.Bytes, matchTime time.Time) (confs uint32, spent bool, err error)
   535  	// ValidateSecret checks that the secret hashes to the secret hash.
   536  	ValidateSecret(secret, secretHash []byte) bool
   537  	// SyncStatus is information about the blockchain sync status. It should
   538  	// only indicate synced when there are network peers and all blocks on the
   539  	// network have been processed by the wallet.
   540  	SyncStatus() (*SyncStatus, error)
   541  	// RegFeeConfirmations gets the confirmations for a registration fee
   542  	// payment. This method need not be supported by all assets. Those assets
   543  	// which do no support DEX registration fees will return an ErrUnsupported.
   544  	RegFeeConfirmations(ctx context.Context, coinID dex.Bytes) (confs uint32, err error)
   545  	// Send sends the exact value to the specified address. This is different
   546  	// from Withdraw, which subtracts the tx fees from the amount sent.
   547  	Send(address string, value, feeRate uint64) (Coin, error)
   548  	// ValidateAddress checks that the provided address is valid.
   549  	ValidateAddress(address string) bool
   550  	// ConfirmRedemption checks the status of a redemption. It returns the
   551  	// number of confirmations the redemption has, the number of confirmations
   552  	// that are required for it to be considered fully confirmed, and the
   553  	// CoinID used to do the redemption. If it is determined that a transaction
   554  	// will not be mined, this function will submit a new transaction to
   555  	// replace the old one. The caller is notified of this by having a
   556  	// different CoinID in the returned asset.ConfirmRedemptionStatus as was
   557  	// used to call the function.
   558  	ConfirmRedemption(coinID dex.Bytes, redemption *Redemption, feeSuggestion uint64) (*ConfirmRedemptionStatus, error)
   559  	// SingleLotSwapRefundFees returns the fees for a swap and refund transaction for a single lot.
   560  	SingleLotSwapRefundFees(version uint32, feeRate uint64, useSafeTxSize bool) (uint64, uint64, error)
   561  	// SingleLotRedeemFees returns the fees for a redeem transaction for a single lot.
   562  	SingleLotRedeemFees(version uint32, feeRate uint64) (uint64, error)
   563  	// StandardSendFee returns the fee for a "standard" send tx.
   564  	StandardSendFee(feeRate uint64) uint64
   565  	// FundMultiOrder funds multiple orders at once. The return values will
   566  	// be in the same order as the passed orders. If less values are returned
   567  	// than the number of orders, then the orders at the end of the list were
   568  	// not able to be funded.
   569  	FundMultiOrder(ord *MultiOrder, maxLock uint64) (coins []Coins, redeemScripts [][]dex.Bytes, fundingFees uint64, err error)
   570  	// MaxFundingFees returns the max fees that could be paid for funding a swap.
   571  	MaxFundingFees(numTrades uint32, feeRate uint64, options map[string]string) uint64
   572  }
   573  
   574  // Authenticator is a wallet implementation that require authentication.
   575  type Authenticator interface {
   576  	// Unlock unlocks the exchange wallet.
   577  	Unlock(pw []byte) error
   578  	// Lock locks the exchange wallet.
   579  	Lock() error
   580  	// Locked will be true if the wallet is currently locked.
   581  	Locked() bool
   582  }
   583  
   584  // TxFeeEstimator is a wallet implementation with fee estimation functionality.
   585  type TxFeeEstimator interface {
   586  	// EstimateSendTxFee returns a tx fee estimate for sending or withdrawing
   587  	// the provided amount using the provided feeRate. This uses actual utxos to
   588  	// calculate the tx fee where possible and ensures the wallet has enough to
   589  	// cover send value and minimum fees.
   590  	EstimateSendTxFee(address string, value, feeRate uint64, subtract, maxWithdraw bool) (fee uint64, isValidAddress bool, err error)
   591  }
   592  
   593  // Broadcaster is a wallet that can send a raw transaction on the asset network.
   594  type Broadcaster interface {
   595  	// SendTransaction broadcasts a raw transaction, returning its coin ID.
   596  	SendTransaction(rawTx []byte) ([]byte, error)
   597  }
   598  
   599  // SyncStatus is the status of wallet syncing.
   600  type SyncStatus struct {
   601  	Synced         bool    `json:"synced"`
   602  	TargetHeight   uint64  `json:"targetHeight"`
   603  	StartingBlocks uint64  `json:"startingBlocks"`
   604  	Blocks         uint64  `json:"blocks"`
   605  	Transactions   *uint64 `json:"txs,omitempty"`
   606  }
   607  
   608  func (ss *SyncStatus) BlockProgress() float32 {
   609  	switch {
   610  	case ss.Synced:
   611  		return 1
   612  	case ss.TargetHeight == 0:
   613  		return 0
   614  	case ss.TargetHeight == ss.StartingBlocks:
   615  		return 0.999
   616  	}
   617  	prog := float32(ss.Blocks-ss.StartingBlocks) / float32(ss.TargetHeight-ss.StartingBlocks)
   618  	if ss.Transactions == nil { // If the asset doesn't support tx sync status, max unsynced is 0.999
   619  		return utils.Min(prog, 0.999)
   620  	}
   621  	return prog
   622  }
   623  
   624  // BondDetails is the return from Bonder.FindBond.
   625  type BondDetails struct {
   626  	*Bond
   627  	LockTime     time.Time
   628  	CheckPrivKey func(priv *secp256k1.PrivateKey) bool
   629  }
   630  
   631  // Bonder is a wallet capable of creating and redeeming time-locked fidelity
   632  // bond transaction outputs.
   633  type Bonder interface {
   634  	Broadcaster
   635  
   636  	// BondsFeeBuffer suggests how much extra may be required for the
   637  	// transaction fees part of bond reserves when bond rotation is enabled.
   638  	// This should return an amount larger than the minimum required by the
   639  	// asset's reserves system for fees, if non-zero, so that a reserves
   640  	// "deficit" does not appear right after the first bond is posted. The
   641  	// caller may provide this value to ReserveBondFunds when actually posting
   642  	// the first bond to ensure it succeeds, assuming balance was checked.
   643  	BondsFeeBuffer(feeRate uint64) uint64
   644  
   645  	// SetReserves sets the bond reserve amount for the wallet.
   646  	SetBondReserves(reserves uint64)
   647  
   648  	// MakeBondTx authors a DEX time-locked fidelity bond transaction for the
   649  	// provided amount, lock time, and dex account ID. An explicit private key
   650  	// type is used to guarantee it's not bytes from something else like a
   651  	// public key. If there are insufficient bond reserves, the returned error
   652  	// should be of kind asset.ErrInsufficientBalance. The returned function may
   653  	// be used to abandon the bond iff it has not yet been broadcast. Generally
   654  	// this means unlocking the funds that are used by the transaction and
   655  	// restoring consumed reserves amounts.
   656  	MakeBondTx(ver uint16, amt, feeRate uint64, lockTime time.Time, privKey *secp256k1.PrivateKey, acctID []byte) (*Bond, func(), error)
   657  	// RefundBond will refund the bond given the full bond output details and
   658  	// private key to spend it. The bond is broadcasted.
   659  	RefundBond(ctx context.Context, ver uint16, coinID, script []byte, amt uint64, privKey *secp256k1.PrivateKey) (Coin, error)
   660  
   661  	// FindBond finds the bond with coinID and returns the values used to
   662  	// create it. The output should be unspent with the lockTime set to
   663  	// some time in the future. searchUntil is used for some wallets that
   664  	// are able to pull blocks.
   665  	FindBond(ctx context.Context, coinID []byte, searchUntil time.Time) (bondDetails *BondDetails, err error)
   666  
   667  	// A RefundBondByCoinID may be created in the future to attempt to refund a
   668  	// bond by locating it on chain, i.e. without providing the amount or
   669  	// script, while also verifying the bond output is unspent. However, it's
   670  	// far more straightforward to generate the refund transaction using the
   671  	// known values. Further, methods for (1) locking coins for future bonds,
   672  	// and (2) renewing bonds by spending a bond directly into a new one, may be
   673  	// required for efficient client bond management.
   674  }
   675  
   676  // Rescanner is a wallet implementation with rescan functionality.
   677  type Rescanner interface {
   678  	// Rescan performs a rescan and block until it is done. If no birthday is
   679  	// provided, internal wallets may use a birthday concurrent with the
   680  	// earliest date at which a wallet was possible, which is asset-dependent.
   681  	Rescan(ctx context.Context, bday /* unix time seconds */ uint64) error
   682  }
   683  
   684  // Recoverer is a wallet implementation with recover functionality.
   685  type Recoverer interface {
   686  	// GetRecoveryCfg returns information that will help the wallet get back to
   687  	// its previous state after it is recreated.
   688  	GetRecoveryCfg() (map[string]string, error)
   689  	// Move will move all wallet files to a backup directory so the wallet can
   690  	// be recreated.
   691  	Move(backupdir string) error
   692  }
   693  
   694  // Withdrawer is a wallet that can withdraw a certain amount from the
   695  // source wallet/account.
   696  type Withdrawer interface {
   697  	// Withdraw withdraws funds to the specified address. Fees are subtracted
   698  	// from the value.
   699  	Withdraw(address string, value, feeRate uint64) (Coin, error)
   700  }
   701  
   702  // Sweeper is a wallet that can clear the entire balance of the wallet/account
   703  // to an address. Similar to Withdraw, but no input value is required.
   704  type Sweeper interface {
   705  	Sweep(address string, feeRate uint64) (Coin, error)
   706  }
   707  
   708  // NewAddresser is a wallet that can generate new deposit addresses.
   709  type NewAddresser interface {
   710  	NewAddress() (string, error)
   711  }
   712  
   713  // AddressReturner is a wallet that allows recycling of unused redemption or refund
   714  // addresses. Asset implementations should log any errors internally. The caller
   715  // is responsible for only returning unused addresses.
   716  type AddressReturner interface {
   717  	// ReturnRefundContracts should be called with the Receipt.Contract() data
   718  	// for any swaps that will not be refunded.
   719  	ReturnRefundContracts(contracts [][]byte)
   720  	// ReturnRedemptionAddress accepts a  Wallet.RedemptionAddress() if the
   721  	// address will not be used.
   722  	ReturnRedemptionAddress(addr string)
   723  }
   724  
   725  // LogFiler is a wallet that allows for downloading of its log file.
   726  type LogFiler interface {
   727  	LogFilePath() string
   728  }
   729  
   730  // DynamicSwapper defines methods that accept an initiation
   731  // or redemption coinID and returns the fee spent on the transaction along with
   732  // the secrets included in the tx. Returns asset.CoinNotFoundError for unmined
   733  // txn. Returns asset.ErrNotEnoughConfirms for txn with too few confirmations.
   734  // Will also error if the secret in the contractData is not found in the
   735  // transaction secrets.
   736  type DynamicSwapper interface {
   737  	// DynamicSwapFeesPaid returns fees for initiation transactions.
   738  	DynamicSwapFeesPaid(ctx context.Context, coinID, contractData dex.Bytes) (fee uint64, secretHashes [][]byte, err error)
   739  	// DynamicRedemptionFeesPaid returns fees for redemption transactions.
   740  	DynamicRedemptionFeesPaid(ctx context.Context, coinID, contractData dex.Bytes) (fee uint64, secretHashes [][]byte, err error)
   741  }
   742  
   743  // FeeRater is capable of retrieving a non-critical fee rate estimate for an
   744  // asset. Some SPV wallets, for example, cannot provide a fee rate estimate, so
   745  // shouldn't implement FeeRater. However, since the mode of external wallets may
   746  // not be known on construction, only connect, a zero rate may be returned. The
   747  // caller should always check for zero and have a fallback rate. The rates from
   748  // FeeRate should be used for rates that are not validated by the server
   749  // Withdraw and Send, and will/should not be used to generate a fee
   750  // suggestion for swap operations.
   751  type FeeRater interface {
   752  	FeeRate() uint64
   753  }
   754  
   755  // FundsMixingStats describes the current state of a wallet's funds mixer.
   756  type FundsMixingStats struct {
   757  	// Enabled is true if the wallet is configured for funds mixing. The wallet
   758  	// must be configured before mixing can be started.
   759  	Enabled bool `json:"enabled"`
   760  	// UnmixedBalanceThreshold is the minimum amount of unmixed funds that must
   761  	// be in the wallet for mixing to happen.
   762  	UnmixedBalanceThreshold uint64 `json:"unmixedBalanceThreshold"`
   763  	// MixedFunds is the total amout of funds in the mixed account.
   764  	MixedFunds uint64 `json:"mixedFunds"`
   765  	// TradingFunds is the total amout of funds in the trading account.
   766  	TradingFunds uint64 `json:"tradingFunds"`
   767  }
   768  
   769  // FundsMixer defines methods for mixing funds in a wallet.
   770  type FundsMixer interface {
   771  	// FundsMixingStats returns the current state of the wallet's funds mixer.
   772  	FundsMixingStats() (*FundsMixingStats, error)
   773  	// ConfigureFundsMixer configures the wallet for funds mixing.
   774  	ConfigureFundsMixer(enabled bool) error
   775  }
   776  
   777  // WalletRestoration contains all the information needed for a user to restore
   778  // their wallet in an external wallet.
   779  type WalletRestoration struct {
   780  	Target string `json:"target"`
   781  	Seed   string `json:"seed"`
   782  	// SeedName is the name of the seed used for this particular wallet, i.e
   783  	// Private Key.
   784  	SeedName     string `json:"seedName"`
   785  	Instructions string `json:"instructions"`
   786  }
   787  
   788  // WalletRestorer is a wallet which gives information about how to restore
   789  // itself in external wallet software.
   790  type WalletRestorer interface {
   791  	// RestorationInfo returns information about how to restore the wallet in
   792  	// various external wallets.
   793  	RestorationInfo(seed []byte) ([]*WalletRestoration, error)
   794  }
   795  
   796  // EarlyAcceleration is returned from the PreAccelerate function to inform the
   797  // user that either their last acceleration or oldest swap transaction happened
   798  // very recently, and that they should double check that they really want to do
   799  // an acceleration.
   800  type EarlyAcceleration struct {
   801  	// TimePast is the amount of seconds that has past since either the previous
   802  	// acceleration, or the oldest unmined swap transaction was submitted to
   803  	// the blockchain.
   804  	TimePast uint64 `json:"timePast"`
   805  	// WasAccelerated is true if the action that took place TimePast seconds
   806  	// ago was an acceleration. If false, the oldest unmined swap transaction
   807  	// in the order was submitted TimePast seconds ago.
   808  	WasAccelerated bool `json:"wasAccelerated"`
   809  }
   810  
   811  // Accelerator is implemented by wallets which support acceleration of the
   812  // mining of swap transactions.
   813  type Accelerator interface {
   814  	// FeesForRemainingSwaps returns the fees for a certain number of
   815  	// chained/grouped swaps at a given feeRate. This should be used with an
   816  	// Accelerator wallet to help compute the required amount for remaining
   817  	// swaps for a given trade with a mix of future and active matches, which is
   818  	// only known to the consumer. This is only accurate if each swap has a
   819  	// single input or chained swaps all pay the same fees. Accurate estimates
   820  	// for new orders without existing funding should use PreSwap or FundOrder.
   821  	FeesForRemainingSwaps(n, feeRate uint64) uint64
   822  	// AccelerateOrder uses the Child-Pays-For-Parent technique to accelerate a
   823  	// chain of swap transactions and previous accelerations. It broadcasts a new
   824  	// transaction with a fee high enough so that the average fee of all the
   825  	// unconfirmed transactions in the chain and the new transaction will have
   826  	// an average fee rate of newFeeRate. The changeCoin argument is the latest
   827  	// change in the order. It must be the input in the acceleration transaction
   828  	// in order for the order to be accelerated. requiredForRemainingSwaps is the
   829  	// amount of funds required to complete the rest of the swaps in the order.
   830  	// The change output of the acceleration transaction will have at least
   831  	// this amount.
   832  	//
   833  	// The returned change coin may be nil, and should be checked before use.
   834  	AccelerateOrder(swapCoins, accelerationCoins []dex.Bytes, changeCoin dex.Bytes,
   835  		requiredForRemainingSwaps, newFeeRate uint64) (Coin, string, error)
   836  	// AccelerationEstimate takes the same parameters as AccelerateOrder, but
   837  	// instead of broadcasting the acceleration transaction, it just returns
   838  	// the amount of funds that will need to be spent in order to increase the
   839  	// average fee rate to the desired amount.
   840  	AccelerationEstimate(swapCoins, accelerationCoins []dex.Bytes, changeCoin dex.Bytes,
   841  		requiredForRemainingSwaps, newFeeRate uint64) (uint64, error)
   842  	// PreAccelerate returns the current average fee rate of the unmined swap
   843  	// initiation and acceleration transactions, and also returns a suggested
   844  	// range that the fee rate should be increased to in order to expedite mining.
   845  	// The feeSuggestion argument is the current prevailing network rate. It is
   846  	// used to help determine the suggestedRange, which is a range meant to give
   847  	// the user a good amount of flexibility in determining the post acceleration
   848  	// effective fee rate, but still not allowing them to pick something
   849  	// outrageously high.
   850  	PreAccelerate(swapCoins, accelerationCoins []dex.Bytes, changeCoin dex.Bytes,
   851  		requiredForRemainingSwaps, feeSuggestion uint64) (uint64, *XYRange, *EarlyAcceleration, error)
   852  }
   853  
   854  // TokenConfig is required to OpenTokenWallet.
   855  type TokenConfig struct {
   856  	// AssetID of the token.
   857  	AssetID uint32
   858  	// Settings correspond to Token.Definition.ConfigOpts.
   859  	Settings map[string]string
   860  	// Emit is a WalletEmitter that manages a channel over which an asset may
   861  	// issue notifications.
   862  	Emit *WalletEmitter
   863  	// PeersChange will be called after the parent's PeersChange.
   864  	PeersChange func(uint32, error)
   865  }
   866  
   867  // TokenMaster is implemented by assets which support degenerate tokens.
   868  type TokenMaster interface {
   869  	// CreateTokenWallet creates a wallet for the specified token asset. The
   870  	// settings correspond to the Token.Definition.ConfigOpts.
   871  	CreateTokenWallet(assetID uint32, settings map[string]string) error
   872  	// OpenTokenWallet opens a wallet for the specified token asset.
   873  	OpenTokenWallet(cfg *TokenConfig) (Wallet, error)
   874  }
   875  
   876  // AccountLocker is a wallet in which redemptions and refunds require a wallet
   877  // to have available balance to pay fees.
   878  type AccountLocker interface {
   879  	// ReserveNRedemption is used when preparing funding for an order that
   880  	// redeems to an account-based asset. The wallet will set aside the
   881  	// appropriate amount of funds so that we can redeem N swaps using the
   882  	// specified fee and asset version. It is an error to request funds >
   883  	// spendable balance.
   884  	ReserveNRedemptions(n uint64, ver uint32, maxFeeRate uint64) (uint64, error)
   885  	// ReReserveRedemption is used when reconstructing existing orders on
   886  	// startup. It is an error to request funds > spendable balance.
   887  	ReReserveRedemption(amt uint64) error
   888  	// UnlockRedemptionReserves is used to return funds reserved for redemption
   889  	// when an order is canceled or otherwise completed unfilled.
   890  	UnlockRedemptionReserves(uint64)
   891  	// ReserveNRefunds is used when preparing funding for an order that refunds
   892  	// to an account-based asset. The wallet will set aside the appropriate
   893  	// amount of funds so that we can refund N swaps using the specified fee and
   894  	// asset version. It is an error to request funds > spendable balance.
   895  	ReserveNRefunds(n uint64, ver uint32, maxFeeRate uint64) (uint64, error)
   896  	// ReReserveRefund is used when reconstructing existing orders on
   897  	// startup. It is an error to request funds > spendable balance.
   898  	ReReserveRefund(uint64) error
   899  	// UnlockRefundReserves is used to return funds reserved for refunds
   900  	// when an order was cancelled or revoked before a swap was initiated,
   901  	// completed successfully, or after a refund was done.
   902  	UnlockRefundReserves(uint64)
   903  }
   904  
   905  // LiveReconfigurer is a wallet that can possibly handle a reconfiguration
   906  // without the need for re-initialization.
   907  type LiveReconfigurer interface {
   908  	// Reconfigure attempts to reconfigure the wallet. If reconfiguration
   909  	// requires a restart, the Wallet should still validate as much
   910  	// configuration as possible.
   911  	Reconfigure(ctx context.Context, cfg *WalletConfig, currentAddress string) (restartRequired bool, err error)
   912  }
   913  
   914  // PeerSource specifies how a wallet knows about a peer. It may have been
   915  // hardcoded into the wallet code, added manually by the user, or discovered
   916  // by communicating with the default/user added peers.
   917  type PeerSource uint16
   918  
   919  const (
   920  	WalletDefault PeerSource = iota
   921  	UserAdded
   922  	Discovered
   923  )
   924  
   925  // WalletPeer provides information about a wallet's peer.
   926  type WalletPeer struct {
   927  	Addr      string     `json:"addr"`
   928  	Source    PeerSource `json:"source"`
   929  	Connected bool       `json:"connected"`
   930  }
   931  
   932  // PeerManager is a wallet which provides allows the user to see the peers the
   933  // wallet is connected to and add new peers.
   934  type PeerManager interface {
   935  	// Peers returns a list of peers that the wallet is connected to.
   936  	Peers() ([]*WalletPeer, error)
   937  	// AddPeer connects the wallet to a new peer. The peer's address will be
   938  	// persisted and connected to each time the wallet is started up.
   939  	AddPeer(addr string) error
   940  	// RemovePeer will remove a peer that was added by AddPeer. This peer may
   941  	// still be connected to by the wallet if it discovers it on its own.
   942  	RemovePeer(addr string) error
   943  }
   944  
   945  type ApprovalStatus uint8
   946  
   947  const (
   948  	Approved ApprovalStatus = iota
   949  	Pending
   950  	NotApproved
   951  )
   952  
   953  // TokenApprover is implemented by wallets that require an approval before
   954  // trading.
   955  type TokenApprover interface {
   956  	// ApproveToken sends an approval transaction for a specific version of
   957  	// the token's swap contract. An error is returned if an approval has
   958  	// already been done or is pending. The onConfirm callback is called
   959  	// when the approval transaction is confirmed.
   960  	ApproveToken(assetVer uint32, onConfirm func()) (string, error)
   961  	// UnapproveToken removes the approval for a specific version of the
   962  	// token's swap contract.
   963  	UnapproveToken(assetVer uint32, onConfirm func()) (string, error)
   964  	// ApprovalStatus returns the approval status for each version of the
   965  	// token's swap contract.
   966  	ApprovalStatus() map[uint32]ApprovalStatus
   967  	// ApprovalFee returns the estimated fee for an approval transaction.
   968  	ApprovalFee(assetVer uint32, approval bool) (uint64, error)
   969  }
   970  
   971  // TicketTransaction represents a ticket transaction.
   972  type TicketTransaction struct {
   973  	Hash        string `json:"hash"`
   974  	TicketPrice uint64 `json:"ticketPrice"`
   975  	Fees        uint64 `json:"fees"`
   976  	Stamp       uint64 `json:"stamp"`
   977  	BlockHeight int64  `json:"blockHeight"`
   978  }
   979  
   980  // TicketStatus from dcrwallet.
   981  type TicketStatus uint
   982  
   983  // Copy of wallet.TicketStatus
   984  const (
   985  	TicketStatusUnknown TicketStatus = iota
   986  	TicketStatusUnmined
   987  	TicketStatusImmature
   988  	TicketStatusLive
   989  	TicketStatusVoted
   990  	TicketStatusMissed
   991  	TicketStatusExpired
   992  	TicketStatusUnspent
   993  	TicketStatusRevoked
   994  )
   995  
   996  // Ticket holds information about a decred ticket.
   997  type Ticket struct {
   998  	Tx      TicketTransaction `json:"tx"`
   999  	Status  TicketStatus      `json:"status"`
  1000  	Spender string            `json:"spender"`
  1001  }
  1002  
  1003  // TBChoice is a possible agenda choice for a TicketBuyer.
  1004  type TBChoice struct {
  1005  	ID          string `json:"id"`
  1006  	Description string `json:"description"`
  1007  }
  1008  
  1009  // TBAgenda is an agenda that the TicketBuyer can vote on.
  1010  type TBAgenda struct {
  1011  	ID            string      `json:"id"`
  1012  	Description   string      `json:"description"`
  1013  	CurrentChoice string      `json:"currentChoice"`
  1014  	Choices       []*TBChoice `json:"choices"`
  1015  }
  1016  
  1017  // TBTreasurySpend represents a treasury spend that the TicketBuyer can vote on.
  1018  type TBTreasurySpend struct {
  1019  	Hash          string `json:"hash"`
  1020  	Value         uint64 `json:"value"`
  1021  	CurrentPolicy string `json:"currentPolicy"`
  1022  }
  1023  
  1024  // Stances are current policy preferences for the TicketBuyer.
  1025  type Stances struct {
  1026  	Agendas        []*TBAgenda                           `json:"agendas"`
  1027  	TreasurySpends []*TBTreasurySpend                    `json:"tspends"`
  1028  	TreasuryKeys   []*dcrwalletjson.TreasuryPolicyResult `json:"treasuryKeys"`
  1029  }
  1030  
  1031  // VotingServiceProvider is information about a voting service provider.
  1032  type VotingServiceProvider struct {
  1033  	URL           string      `json:"url"`
  1034  	Network       dex.Network `json:"network"`
  1035  	Launched      uint64      `json:"launched"`    // milliseconds
  1036  	LastUpdated   uint64      `json:"lastUpdated"` // milliseconds
  1037  	APIVersions   []int64     `json:"apiVersions"`
  1038  	FeePercentage float64     `json:"feePercentage"`
  1039  	Closed        bool        `json:"closed"`
  1040  	Voting        int64       `json:"voting"`
  1041  	Voted         int64       `json:"voted"`
  1042  	Revoked       int64       `json:"revoked"`
  1043  	VSPDVersion   string      `json:"vspdVersion"`
  1044  	BlockHeight   uint32      `json:"blockHeight"`
  1045  	NetShare      float32     `json:"netShare"`
  1046  }
  1047  
  1048  // TicketStats sums up some statistics for historical staking data for a
  1049  // TicketBuyer.
  1050  type TicketStats struct {
  1051  	TotalRewards uint64 `json:"totalRewards"`
  1052  	TicketCount  uint32 `json:"ticketCount"`
  1053  	Votes        uint32 `json:"votes"`
  1054  	Revokes      uint32 `json:"revokes"`
  1055  	Queued       uint32 `json:"queued"`
  1056  	Mempool      uint32 `json:"mempool"`
  1057  }
  1058  
  1059  // TicketStakingStatus holds various stake information from the wallet.
  1060  type TicketStakingStatus struct {
  1061  	// TicketPrice is the current price of one ticket. Also known as the
  1062  	// stake difficulty.
  1063  	TicketPrice uint64 `json:"ticketPrice"`
  1064  	// VotingSubsidy is the current reward for a vote.
  1065  	VotingSubsidy uint64 `json:"votingSubsidy"`
  1066  	// VSP is the currently set VSP address and fee.
  1067  	VSP string `json:"vsp"`
  1068  	// IsRPC will be true if this is an RPC wallet, in which case we can't
  1069  	// set a new VSP and some other information may not be available.
  1070  	IsRPC bool `json:"isRPC"`
  1071  	// Tickets returns current active tickets up until they are voted or
  1072  	// revoked. Includes unconfirmed tickets.
  1073  	Tickets []*Ticket `json:"tickets"`
  1074  	// Stances returns current voting preferences.
  1075  	Stances Stances `json:"stances"`
  1076  	// Stats is statistical info about staking history.
  1077  	Stats TicketStats `json:"stats"`
  1078  }
  1079  
  1080  // TicketBuyer is a wallet that can participate in decred staking.
  1081  //
  1082  // TODO: Consider adding (*AutoClient).ProcessUnprocessedTickets/ProcessManagedTickets
  1083  // to be used when restoring wallet from seed.
  1084  type TicketBuyer interface {
  1085  	// StakeStatus returns current staking statuses such as currently owned
  1086  	// tickets, ticket price, and current voting preferences.
  1087  	StakeStatus() (*TicketStakingStatus, error)
  1088  	// SetVSP sets the VSP provider.
  1089  	SetVSP(addr string) error
  1090  	// PurchaseTickets starts an aysnchronous process to purchase n tickets.
  1091  	// Look for TicketPurchaseUpdate notifications to track the process.
  1092  	PurchaseTickets(n int, feeSuggestion uint64) error
  1093  	// SetVotingPreferences sets default voting settings for all active
  1094  	// tickets and future tickets. Nil maps can be provided for no change.
  1095  	SetVotingPreferences(choices, tSpendPolicy, treasuryPolicy map[string]string) error
  1096  	// ListVSPs lists known available voting service providers.
  1097  	ListVSPs() ([]*VotingServiceProvider, error)
  1098  	// TicketPage fetches a page of tickets within a range of block numbers with
  1099  	// a target page size and optional offset. scanStart is the block in which
  1100  	// to start the scan. The scan progresses in reverse block number order,
  1101  	// starting at scanStart and going to progressively lower blocks. scanStart
  1102  	// can be set to -1 to indicate the current chain tip.
  1103  	TicketPage(scanStart int32, n, skipN int) ([]*Ticket, error)
  1104  }
  1105  
  1106  // TransactionType is the type of transaction made by a wallet.
  1107  type TransactionType uint16
  1108  
  1109  const (
  1110  	Unknown TransactionType = iota
  1111  	Send
  1112  	Receive
  1113  	Swap
  1114  	Redeem
  1115  	Refund
  1116  	Split
  1117  	CreateBond
  1118  	RedeemBond
  1119  	ApproveToken
  1120  	Acceleration
  1121  	SelfSend
  1122  	RevokeTokenApproval
  1123  	TicketPurchase
  1124  	TicketVote
  1125  	TicketRevocation
  1126  	// SwapOrSend is used when a wallet scanned its historical transactions
  1127  	// and was unable to determine if the transaction was a swap or a send.
  1128  	SwapOrSend
  1129  	Mix
  1130  )
  1131  
  1132  // IncomingTxType returns true if the wallet's balance increases due to a
  1133  // transaction.
  1134  func IncomingTxType(txType TransactionType) bool {
  1135  	return txType == Receive || txType == Redeem || txType == Refund || txType == RedeemBond
  1136  }
  1137  
  1138  // BondTxInfo contains information about a CreateBond or RedeemBond
  1139  // transaction.
  1140  type BondTxInfo struct {
  1141  	// AccountID is the account is the account ID that the bond is applied to.
  1142  	AccountID dex.Bytes `json:"accountID"`
  1143  	// LockTime is the time until which the bond is locked.
  1144  	LockTime uint64 `json:"lockTime"`
  1145  	// BondID is the ID of the bond.
  1146  	BondID dex.Bytes `json:"bondID"`
  1147  }
  1148  
  1149  // WalletTransaction represents a transaction that was made by a wallet.
  1150  type WalletTransaction struct {
  1151  	Type   TransactionType `json:"type"`
  1152  	ID     string          `json:"id"`
  1153  	Amount uint64          `json:"amount"`
  1154  	Fees   uint64          `json:"fees"`
  1155  	// BlockNumber is 0 for txs in the mempool.
  1156  	BlockNumber uint64 `json:"blockNumber"`
  1157  	// Timestamp is the time the transaction was mined.
  1158  	Timestamp uint64 `json:"timestamp"`
  1159  	// TokenID will be non-nil if the BalanceDelta applies to the balance
  1160  	// of a token.
  1161  	TokenID *uint32 `json:"tokenID,omitempty"`
  1162  	// Recipient will be non-nil for Send/Receive transactions, and specifies the
  1163  	// recipient address of the transaction.
  1164  	Recipient *string `json:"recipient,omitempty"`
  1165  	// BondInfo will be non-nil for CreateBond and RedeemBond transactions.
  1166  	BondInfo *BondTxInfo `json:"bondInfo,omitempty"`
  1167  	// AdditionalData contains asset specific information, i.e. nonce
  1168  	// for ETH.
  1169  	AdditionalData map[string]string `json:"additionalData"`
  1170  	// Confirmed is true when the transaction is considered finalized.
  1171  	// Confirmed transactions are no longer updated and will be considered
  1172  	// finalized forever.
  1173  	Confirmed bool `json:"confirmed"`
  1174  	// Rejected will be true the transaction was rejected and did not have any
  1175  	// effect, though fees were incurred.
  1176  	Rejected bool `json:"rejected,omitempty"`
  1177  }
  1178  
  1179  // WalletHistorian is a wallet that is able to retrieve the history of all
  1180  // transactions it has made.
  1181  type WalletHistorian interface {
  1182  	// TxHistory returns all the transactions a wallet has made. If refID
  1183  	// is nil, then transactions starting from the most recent are returned
  1184  	// (past is ignored). If past is true, the transactions prior to the
  1185  	// refID are returned, otherwise the transactions after the refID are
  1186  	// returned. n is the number of transactions to return. If n is <= 0,
  1187  	// all the transactions will be returned.
  1188  	TxHistory(n int, refID *string, past bool) ([]*WalletTransaction, error)
  1189  	// WalletTransaction returns a single transaction that either a wallet
  1190  	// has made or in which the wallet has received funds. This function may
  1191  	// support more transactions than are returned by TxHistory. For example,
  1192  	// ETH/token wallets do not return receiving transactions in TxHistory,
  1193  	// but WalletTransaction will return them.
  1194  	WalletTransaction(ctx context.Context, txID string) (*WalletTransaction, error)
  1195  }
  1196  
  1197  // Bond is the fidelity bond info generated for a certain account ID, amount,
  1198  // and lock time. These data are intended for the "post bond" request, in which
  1199  // the server pre-validates the unsigned transaction, the client then publishes
  1200  // the corresponding signed transaction, and a final request is made once the
  1201  // bond is fully confirmed. The caller should manage the private key.
  1202  type Bond struct {
  1203  	Version uint16
  1204  	AssetID uint32
  1205  	Amount  uint64
  1206  	CoinID  []byte
  1207  	Data    []byte // additional data to interpret the bond e.g. redeem script, bond contract, etc.
  1208  	// SignedTx and UnsignedTx are the opaque (raw bytes) signed and unsigned
  1209  	// bond creation transactions, in whatever encoding and funding scheme for
  1210  	// this asset and wallet. The unsigned one is used to pre-validate this bond
  1211  	// with the server prior to publishing it, thus locking funds for a long
  1212  	// period of time. Once the bond is pre-validated, the signed tx may then be
  1213  	// published by the wallet.
  1214  	SignedTx, UnsignedTx []byte
  1215  	// RedeemTx is a backup transaction that spends the bond output. Normally
  1216  	// the a key index will be used to derive the key when the bond expires.
  1217  	RedeemTx []byte
  1218  }
  1219  
  1220  // Balance is categorized information about a wallet's balance.
  1221  type Balance struct {
  1222  	// Available is the balance that is available for trading immediately.
  1223  	Available uint64 `json:"available"`
  1224  	// Immature is the balance that is not ready, but will be after some
  1225  	// confirmations.
  1226  	Immature uint64 `json:"immature"`
  1227  	// Locked is the total amount locked in the wallet which includes but
  1228  	// is not limited to funds locked for swap but not actually swapped yet.
  1229  	Locked uint64 `json:"locked"`
  1230  	// BondReserves is the amount of funds locked in the wallet for expenses
  1231  	// associated with bond maintenance.
  1232  	BondReserves uint64 `json:"bondReserves"`
  1233  	// ReservesDeficit is the difference between the available balance and the
  1234  	// amount reserved for specific purposes.
  1235  	ReservesDeficit uint64 `json:"reservesDeficit"`
  1236  	// Other is a place to list custom balance categories. It is recommended for
  1237  	// custom balance added here to have a translation and tooltip info in
  1238  	// client/webserver/site/src/js/wallet.js#customWalletBalanceCategory
  1239  	Other map[BalanceCategory]CustomBalance `json:"other"`
  1240  }
  1241  
  1242  // CustomBalance is a balance category used to track funds for a particular
  1243  // purpose or for a special kind of balance (e.g Zcash Shielded wallet
  1244  // balances).
  1245  type CustomBalance struct {
  1246  	// Amount is the balance in the wallet for this custom category. It is
  1247  	// subtracted from Balance.Available above.
  1248  	Amount uint64 `json:"amt"`
  1249  	// Locked is a flag to indicate that this Amount is not included in
  1250  	// Balance.Available and is included in Balance.Locked.
  1251  	Locked bool `json:"locked"`
  1252  }
  1253  
  1254  // BalanceCategory is a string identifier for a custom balance category.
  1255  type BalanceCategory string
  1256  
  1257  // Balance categories for custom balances These values are used as a map key for
  1258  // custom balances and may be recognized in the frontend to support translation.
  1259  // It is recommended for custom balance categories listed here to have a
  1260  // translation and tooltip info in
  1261  // client/webserver/site/src/js/wallet.js#customWalletBalanceCategory. If any of
  1262  // these balance categories should change, the customWalletBalanceCategory
  1263  // function in the wallet.js file above should be updated with the new value.
  1264  const (
  1265  	BalanceCategoryShielded = "Shielded"
  1266  	BalanceCategoryUnmixed  = "Unmixed"
  1267  	BalanceCategoryStaked   = "Staked"
  1268  )
  1269  
  1270  // Coin is some amount of spendable asset. Coin provides the information needed
  1271  // to locate the unspent value on the blockchain.
  1272  type Coin interface {
  1273  	// ID is a unique identifier for this coin.
  1274  	ID() dex.Bytes
  1275  	// String is a string representation of the coin.
  1276  	String() string
  1277  	// Value is the available quantity, in atoms/satoshi.
  1278  	Value() uint64
  1279  	// TxID is the ID of the transaction that created the coin.
  1280  	TxID() string
  1281  }
  1282  
  1283  // TokenCoin extends the Coin interface to include the amount locked
  1284  // of the parent asset to be used for fees.
  1285  type TokenCoin interface {
  1286  	Coin
  1287  	Fees() uint64
  1288  }
  1289  
  1290  type RecoveryCoin interface {
  1291  	// RecoveryID is an ID that can be used to re-establish funding state during
  1292  	// startup. If a Coin implements RecoveryCoin, the RecoveryID will be used
  1293  	// in the database record, and ultimately passed to the FundingCoins method.
  1294  	RecoveryID() dex.Bytes
  1295  }
  1296  
  1297  // Coins a collection of coins as returned by Fund.
  1298  type Coins []Coin
  1299  
  1300  // Receipt holds information about a sent swap contract.
  1301  type Receipt interface {
  1302  	// Expiration is the time lock expiration.
  1303  	Expiration() time.Time
  1304  	// Coin is the swap initiation transaction's Coin.
  1305  	Coin() Coin
  1306  	// Contract is the unique swap contract data. This may be a redeem script
  1307  	// for UTXO assets, or other information that uniquely identifies the swap
  1308  	// for account-based assets e.g. a contract version and secret hash for ETH.
  1309  	Contract() dex.Bytes
  1310  	// String provides a human-readable representation of the swap that may
  1311  	// provide supplementary data to locate the swap.
  1312  	String() string
  1313  	// SignedRefund is a signed refund script that can be used to return
  1314  	// funds to the user in the case a contract expires.
  1315  	SignedRefund() dex.Bytes
  1316  }
  1317  
  1318  // AuditInfo is audit information about a swap contract needed to audit the
  1319  // contract.
  1320  type AuditInfo struct {
  1321  	// Recipient is the string-encoded recipient address.
  1322  	Recipient string
  1323  	// Expiration is the unix timestamp of the contract time lock expiration.
  1324  	Expiration time.Time
  1325  	// Coin is the coin that contains the contract.
  1326  	Coin Coin
  1327  	// Contract is the unique swap contract data. This may be a redeem script
  1328  	// for UTXO assets, or other information that uniquely identifies the swap
  1329  	// for account-based assets e.g. a contract version and secret hash for ETH.
  1330  	Contract dex.Bytes
  1331  	// SecretHash is the contract's secret hash. This is likely to be encoded in
  1332  	// the Contract field, which is often the redeem script or an asset-specific
  1333  	// encoding of the unique swap data.
  1334  	SecretHash dex.Bytes
  1335  }
  1336  
  1337  // INPUT TYPES
  1338  // The types below will be used by the client as inputs for the methods exposed
  1339  // by the wallet.
  1340  
  1341  // Swaps is the details needed to broadcast a swap contract(s).
  1342  type Swaps struct {
  1343  	// Version is the asset version.
  1344  	Version uint32
  1345  	// Inputs are the Coins being spent.
  1346  	Inputs Coins
  1347  	// Contract is the contract data.
  1348  	Contracts []*Contract
  1349  	// FeeRate is the required fee rate in atoms/byte.
  1350  	FeeRate uint64
  1351  	// LockChange can be set to true if the change should be locked for
  1352  	// subsequent matches.
  1353  	LockChange bool
  1354  	// Options are OrderOptions set or selected by the user at order time.
  1355  	Options map[string]string
  1356  }
  1357  
  1358  // Contract is a swap contract.
  1359  type Contract struct {
  1360  	// Address is the receiving address.
  1361  	Address string
  1362  	// Value is the amount being traded.
  1363  	Value uint64
  1364  	// SecretHash is the hash of the secret key.
  1365  	SecretHash dex.Bytes
  1366  	// LockTime is the contract lock time in UNIX seconds.
  1367  	LockTime uint64
  1368  }
  1369  
  1370  // Redemption is a redemption transaction that spends a counter-party's swap
  1371  // contract.
  1372  type Redemption struct {
  1373  	// Spends is the AuditInfo for the swap output being spent.
  1374  	Spends *AuditInfo
  1375  	// Secret is the secret key needed to satisfy the swap contract.
  1376  	Secret dex.Bytes
  1377  }
  1378  
  1379  // RedeemForm is a group of Redemptions. The struct will be
  1380  // expanded in in-progress work to accommodate order-time options.
  1381  type RedeemForm struct {
  1382  	Redemptions []*Redemption
  1383  	// FeeSuggestion is a suggested fee rate. For redemptions, the suggestion is
  1384  	// just a fallback if an internal estimate using the wallet's redeem confirm
  1385  	// block target setting is not available. Since this is the redemption,
  1386  	// there is no obligation on the client to use the fee suggestion in any
  1387  	// way, but obviously fees that are too low may result in the redemption
  1388  	// getting stuck in mempool.
  1389  	FeeSuggestion uint64
  1390  	Options       map[string]string
  1391  }
  1392  
  1393  // Order is order details needed for FundOrder.
  1394  type Order struct {
  1395  	// Version is the asset version of the "from" asset with the init
  1396  	// transaction.
  1397  	Version uint32
  1398  	// Value is the amount required to satisfy the order. The Value does not
  1399  	// include fees. Fees will be calculated internally based on the number of
  1400  	// possible swaps (MaxSwapCount) and the exchange's configuration
  1401  	// (Exchange).
  1402  	Value uint64
  1403  	// MaxSwapCount is the number of lots in the order, which is also the
  1404  	// maximum number of transactions that an order could potentially generate
  1405  	// in a worst-case scenario of all 1-lot matches.
  1406  	MaxSwapCount uint64 // uint64 for compatibility with quantity and lot size.
  1407  	// MaxFeeRate is the largest possible fee rate for the init transaction (of
  1408  	// this "from" asset) specific to and provided by a particular server, and
  1409  	// is used to calculate the funding required to cover fees.
  1410  	MaxFeeRate uint64
  1411  	// Immediate should be set to true if this is for an order that is not a
  1412  	// standing order, likely a market order or a limit order with immediate
  1413  	// time-in-force.
  1414  	Immediate bool
  1415  	// FeeSuggestion is a suggested fee from the server. If a split transaction
  1416  	// is used, the fee rate used should be at least the suggested fee, else
  1417  	// zero-conf coins might be rejected.
  1418  	FeeSuggestion uint64
  1419  	// Options are options that corresponds to PreSwap.Options, as well as
  1420  	// their values.
  1421  	Options map[string]string
  1422  
  1423  	// The following fields are only used for some assets where the redeemed/to
  1424  	// asset may require funds in this "from" asset. For example, buying ERC20
  1425  	// tokens with ETH.
  1426  
  1427  	// RedeemVersion is the asset version of the "to" asset with the redeem
  1428  	// transaction.
  1429  	RedeemVersion uint32
  1430  	// RedeemAssetID is the asset ID of the "to" asset.
  1431  	RedeemAssetID uint32
  1432  }
  1433  
  1434  // MultiOrderValue is one of the placements in a multi-order.
  1435  type MultiOrderValue struct {
  1436  	// Value is the amount required to satisfy the order. The Value does not
  1437  	// include fees. Fees will be calculated internally based on the number of
  1438  	// possible swaps (MaxSwapCount) and the exchange's configuration
  1439  	// (Exchange).
  1440  	Value uint64
  1441  	// MaxSwapCount is the number of lots in the order, which is also the
  1442  	// maximum number of transactions that an order could potentially generate
  1443  	// in a worst-case scenario of all 1-lot matches.
  1444  	MaxSwapCount uint64 // uint64 for compatibility with quantity and lot size.
  1445  }
  1446  
  1447  // MultiOrder is order details needed for FundMultiOrder.
  1448  type MultiOrder struct {
  1449  	// Version is the asset version of the "from" asset with the init
  1450  	// transaction.
  1451  	Version uint32
  1452  	Values  []*MultiOrderValue
  1453  	// MaxFeeRate is the largest possible fee rate for the init transaction (of
  1454  	// this "from" asset) specific to and provided by a particular server, and
  1455  	// is used to calculate the funding required to cover fees.
  1456  	MaxFeeRate uint64
  1457  	// FeeSuggestion is a suggested fee from the server. If a split transaction
  1458  	// is used, the fee rate used should be at least the suggested fee, else
  1459  	// zero-conf coins might be rejected.
  1460  	FeeSuggestion uint64
  1461  	// Options are options that corresponds to PreSwap.Options, as well as
  1462  	// their values.
  1463  	Options map[string]string
  1464  
  1465  	// The following fields are only used for some assets where the redeemed/to
  1466  	// asset may require funds in this "from" asset. For example, buying ERC20
  1467  	// tokens with ETH.
  1468  
  1469  	// RedeemVersion is the asset version of the "to" asset with the redeem
  1470  	// transaction.
  1471  	RedeemVersion uint32
  1472  	// RedeemAssetID is the asset ID of the "to" asset.
  1473  	RedeemAssetID uint32
  1474  }
  1475  
  1476  // A GeocodeRedeemer redeems funds from a geocode game.
  1477  type GeocodeRedeemer interface {
  1478  	RedeemGeocode(code []byte, msg string) (dex.Bytes, uint64, error)
  1479  }
  1480  
  1481  // WalletNotification can be any asynchronous information the wallet needs
  1482  // to convey.
  1483  type WalletNotification any
  1484  
  1485  type baseWalletNotification struct {
  1486  	AssetID uint32 `json:"assetID"`
  1487  	Route   string `json:"route"`
  1488  }
  1489  
  1490  // TipChangeNote is the only required wallet notification. All wallets should
  1491  // emit a TipChangeNote when a state change occurs that might necessitate swap
  1492  // progression or new balance checks.
  1493  type TipChangeNote struct {
  1494  	baseWalletNotification
  1495  	Tip  uint64 `json:"tip"`
  1496  	Data any    `json:"data"`
  1497  }
  1498  
  1499  // BalanceChangeNote can be sent when the wallet detects a balance change
  1500  // between tip changes.
  1501  type BalanceChangeNote struct {
  1502  	baseWalletNotification
  1503  	Balance *Balance
  1504  }
  1505  
  1506  // TransactionNote is sent when a transaction is made, seen, or updated.
  1507  type TransactionNote struct {
  1508  	baseWalletNotification
  1509  	Transaction *WalletTransaction `json:"transaction"`
  1510  	New         bool               `json:"new"`
  1511  }
  1512  
  1513  // CustomWalletNote is any other information the wallet wishes to convey to
  1514  // the user.
  1515  type CustomWalletNote struct {
  1516  	baseWalletNotification
  1517  	Payload any `json:"payload"`
  1518  }
  1519  
  1520  type ActionTaker interface {
  1521  	// TakeAction processes a response to an ActionRequired wallet notification.
  1522  	TakeAction(actionID string, payload []byte) error
  1523  }
  1524  
  1525  // WalletEmitter handles a channel for wallet notifications and provides methods
  1526  // that generates notifications.
  1527  type WalletEmitter struct {
  1528  	c       chan<- WalletNotification
  1529  	assetID uint32
  1530  	log     dex.Logger
  1531  }
  1532  
  1533  type ActionRequiredNote struct {
  1534  	baseWalletNotification
  1535  	Payload  any    `json:"payload"`
  1536  	UniqueID string `json:"uniqueID"`
  1537  	ActionID string `json:"actionID"`
  1538  }
  1539  
  1540  type ActionResolvedNote struct {
  1541  	baseWalletNotification
  1542  	UniqueID string `json:"uniqueID"`
  1543  }
  1544  
  1545  // NewWalletEmitter constructs a WalletEmitter for an asset.
  1546  func NewWalletEmitter(c chan<- WalletNotification, assetID uint32, log dex.Logger) *WalletEmitter {
  1547  	return &WalletEmitter{
  1548  		c:       c,
  1549  		assetID: assetID,
  1550  		log:     log,
  1551  	}
  1552  }
  1553  
  1554  func (e *WalletEmitter) emit(note WalletNotification) {
  1555  	select {
  1556  	case e.c <- note:
  1557  	default:
  1558  		e.log.Warn("blocking WalletNotification channel")
  1559  	}
  1560  }
  1561  
  1562  // Data sends a CustomWalletNote with the specified data payload.
  1563  func (e *WalletEmitter) Data(route string, payload any) {
  1564  	e.emit(&CustomWalletNote{
  1565  		baseWalletNotification: baseWalletNotification{
  1566  			AssetID: e.assetID,
  1567  			Route:   route,
  1568  		}, Payload: payload,
  1569  	})
  1570  }
  1571  
  1572  // TipChange sends a TipChangeNote with optional extra data.
  1573  func (e *WalletEmitter) TipChange(tip uint64, datas ...any) {
  1574  	var data any
  1575  	if len(datas) > 0 {
  1576  		data = datas[0]
  1577  	}
  1578  	e.emit(&TipChangeNote{
  1579  		baseWalletNotification: baseWalletNotification{
  1580  			AssetID: e.assetID,
  1581  			Route:   "tipChange",
  1582  		},
  1583  		Tip: tip, Data: data,
  1584  	})
  1585  }
  1586  
  1587  // BalanceChange sends a BalanceChangeNote.
  1588  func (e *WalletEmitter) BalanceChange(bal *Balance) {
  1589  	e.emit(&BalanceChangeNote{
  1590  		baseWalletNotification: baseWalletNotification{
  1591  			AssetID: e.assetID,
  1592  			Route:   "balanceChange",
  1593  		},
  1594  		Balance: bal,
  1595  	})
  1596  }
  1597  
  1598  // TransactionNote sends a TransactionNote.
  1599  func (e *WalletEmitter) TransactionNote(tx *WalletTransaction, new bool) {
  1600  	e.emit(&TransactionNote{
  1601  		baseWalletNotification: baseWalletNotification{
  1602  			AssetID: e.assetID,
  1603  			Route:   "transaction",
  1604  		},
  1605  		Transaction: tx,
  1606  		New:         new,
  1607  	})
  1608  }
  1609  
  1610  // TransactionHistorySyncedNote sends a TransactionHistorySyncedNote.
  1611  func (e *WalletEmitter) TransactionHistorySyncedNote() {
  1612  	e.emit(&baseWalletNotification{
  1613  		AssetID: e.assetID,
  1614  		Route:   "transactionHistorySynced",
  1615  	})
  1616  }
  1617  
  1618  // ActionRequired is a route that will end up as a special dialogue seeking
  1619  // user input via (ActionTaker).TakeAction.
  1620  func (e *WalletEmitter) ActionRequired(uniqueID, actionID string, payload any) {
  1621  	e.emit(&ActionRequiredNote{
  1622  		baseWalletNotification: baseWalletNotification{
  1623  			AssetID: e.assetID,
  1624  			Route:   "actionRequired",
  1625  		},
  1626  		UniqueID: uniqueID,
  1627  		ActionID: actionID,
  1628  		Payload:  payload,
  1629  	})
  1630  }
  1631  
  1632  func (e *WalletEmitter) ActionResolved(uniqueID string) {
  1633  	e.emit(&ActionResolvedNote{
  1634  		baseWalletNotification: baseWalletNotification{
  1635  			AssetID: e.assetID,
  1636  			Route:   "actionResolved",
  1637  		},
  1638  		UniqueID: uniqueID,
  1639  	})
  1640  }