decred.org/dcrdex@v1.0.5/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/v5/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 string `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  	AddressUsed(string) (bool, error)
   712  }
   713  
   714  // AddressReturner is a wallet that allows recycling of unused redemption or refund
   715  // addresses. Asset implementations should log any errors internally. The caller
   716  // is responsible for only returning unused addresses.
   717  type AddressReturner interface {
   718  	// ReturnRefundContracts should be called with the Receipt.Contract() data
   719  	// for any swaps that will not be refunded.
   720  	ReturnRefundContracts(contracts [][]byte)
   721  	// ReturnRedemptionAddress accepts a  Wallet.RedemptionAddress() if the
   722  	// address will not be used.
   723  	ReturnRedemptionAddress(addr string)
   724  }
   725  
   726  // LogFiler is a wallet that allows for downloading of its log file.
   727  type LogFiler interface {
   728  	LogFilePath() string
   729  }
   730  
   731  // DynamicSwapper defines methods that accept an initiation
   732  // or redemption coinID and returns the fee spent on the transaction along with
   733  // the secrets included in the tx. Returns asset.CoinNotFoundError for unmined
   734  // txn. Returns asset.ErrNotEnoughConfirms for txn with too few confirmations.
   735  // Will also error if the secret in the contractData is not found in the
   736  // transaction secrets.
   737  type DynamicSwapper interface {
   738  	// DynamicSwapFeesPaid returns fees for initiation transactions.
   739  	DynamicSwapFeesPaid(ctx context.Context, coinID, contractData dex.Bytes) (fee uint64, secretHashes [][]byte, err error)
   740  	// DynamicRedemptionFeesPaid returns fees for redemption transactions.
   741  	DynamicRedemptionFeesPaid(ctx context.Context, coinID, contractData dex.Bytes) (fee uint64, secretHashes [][]byte, err error)
   742  }
   743  
   744  // FeeRater is capable of retrieving a non-critical fee rate estimate for an
   745  // asset. Some SPV wallets, for example, cannot provide a fee rate estimate, so
   746  // shouldn't implement FeeRater. However, since the mode of external wallets may
   747  // not be known on construction, only connect, a zero rate may be returned. The
   748  // caller should always check for zero and have a fallback rate. The rates from
   749  // FeeRate should be used for rates that are not validated by the server
   750  // Withdraw and Send, and will/should not be used to generate a fee
   751  // suggestion for swap operations.
   752  type FeeRater interface {
   753  	FeeRate() uint64
   754  }
   755  
   756  // FundsMixingStats describes the current state of a wallet's funds mixer.
   757  type FundsMixingStats struct {
   758  	// Enabled is true if the wallet is configured for funds mixing. The wallet
   759  	// must be configured before mixing can be started.
   760  	Enabled bool `json:"enabled"`
   761  	// UnmixedBalanceThreshold is the minimum amount of unmixed funds that must
   762  	// be in the wallet for mixing to happen.
   763  	UnmixedBalanceThreshold uint64 `json:"unmixedBalanceThreshold"`
   764  	// MixedFunds is the total amout of funds in the mixed account.
   765  	MixedFunds uint64 `json:"mixedFunds"`
   766  	// TradingFunds is the total amout of funds in the trading account.
   767  	TradingFunds uint64 `json:"tradingFunds"`
   768  }
   769  
   770  // FundsMixer defines methods for mixing funds in a wallet.
   771  type FundsMixer interface {
   772  	// FundsMixingStats returns the current state of the wallet's funds mixer.
   773  	FundsMixingStats() (*FundsMixingStats, error)
   774  	// ConfigureFundsMixer configures the wallet for funds mixing.
   775  	ConfigureFundsMixer(enabled bool) error
   776  }
   777  
   778  // WalletRestoration contains all the information needed for a user to restore
   779  // their wallet in an external wallet.
   780  type WalletRestoration struct {
   781  	Target string `json:"target"`
   782  	Seed   string `json:"seed"`
   783  	// SeedName is the name of the seed used for this particular wallet, i.e
   784  	// Private Key.
   785  	SeedName     string `json:"seedName"`
   786  	Instructions string `json:"instructions"`
   787  }
   788  
   789  // WalletRestorer is a wallet which gives information about how to restore
   790  // itself in external wallet software.
   791  type WalletRestorer interface {
   792  	// RestorationInfo returns information about how to restore the wallet in
   793  	// various external wallets.
   794  	RestorationInfo(seed []byte) ([]*WalletRestoration, error)
   795  }
   796  
   797  // EarlyAcceleration is returned from the PreAccelerate function to inform the
   798  // user that either their last acceleration or oldest swap transaction happened
   799  // very recently, and that they should double check that they really want to do
   800  // an acceleration.
   801  type EarlyAcceleration struct {
   802  	// TimePast is the amount of seconds that has past since either the previous
   803  	// acceleration, or the oldest unmined swap transaction was submitted to
   804  	// the blockchain.
   805  	TimePast uint64 `json:"timePast"`
   806  	// WasAccelerated is true if the action that took place TimePast seconds
   807  	// ago was an acceleration. If false, the oldest unmined swap transaction
   808  	// in the order was submitted TimePast seconds ago.
   809  	WasAccelerated bool `json:"wasAccelerated"`
   810  }
   811  
   812  // Accelerator is implemented by wallets which support acceleration of the
   813  // mining of swap transactions.
   814  type Accelerator interface {
   815  	// FeesForRemainingSwaps returns the fees for a certain number of
   816  	// chained/grouped swaps at a given feeRate. This should be used with an
   817  	// Accelerator wallet to help compute the required amount for remaining
   818  	// swaps for a given trade with a mix of future and active matches, which is
   819  	// only known to the consumer. This is only accurate if each swap has a
   820  	// single input or chained swaps all pay the same fees. Accurate estimates
   821  	// for new orders without existing funding should use PreSwap or FundOrder.
   822  	FeesForRemainingSwaps(n, feeRate uint64) uint64
   823  	// AccelerateOrder uses the Child-Pays-For-Parent technique to accelerate a
   824  	// chain of swap transactions and previous accelerations. It broadcasts a new
   825  	// transaction with a fee high enough so that the average fee of all the
   826  	// unconfirmed transactions in the chain and the new transaction will have
   827  	// an average fee rate of newFeeRate. The changeCoin argument is the latest
   828  	// change in the order. It must be the input in the acceleration transaction
   829  	// in order for the order to be accelerated. requiredForRemainingSwaps is the
   830  	// amount of funds required to complete the rest of the swaps in the order.
   831  	// The change output of the acceleration transaction will have at least
   832  	// this amount.
   833  	//
   834  	// The returned change coin may be nil, and should be checked before use.
   835  	AccelerateOrder(swapCoins, accelerationCoins []dex.Bytes, changeCoin dex.Bytes,
   836  		requiredForRemainingSwaps, newFeeRate uint64) (Coin, string, error)
   837  	// AccelerationEstimate takes the same parameters as AccelerateOrder, but
   838  	// instead of broadcasting the acceleration transaction, it just returns
   839  	// the amount of funds that will need to be spent in order to increase the
   840  	// average fee rate to the desired amount.
   841  	AccelerationEstimate(swapCoins, accelerationCoins []dex.Bytes, changeCoin dex.Bytes,
   842  		requiredForRemainingSwaps, newFeeRate uint64) (uint64, error)
   843  	// PreAccelerate returns the current average fee rate of the unmined swap
   844  	// initiation and acceleration transactions, and also returns a suggested
   845  	// range that the fee rate should be increased to in order to expedite mining.
   846  	// The feeSuggestion argument is the current prevailing network rate. It is
   847  	// used to help determine the suggestedRange, which is a range meant to give
   848  	// the user a good amount of flexibility in determining the post acceleration
   849  	// effective fee rate, but still not allowing them to pick something
   850  	// outrageously high.
   851  	PreAccelerate(swapCoins, accelerationCoins []dex.Bytes, changeCoin dex.Bytes,
   852  		requiredForRemainingSwaps, feeSuggestion uint64) (uint64, *XYRange, *EarlyAcceleration, error)
   853  }
   854  
   855  // TokenConfig is required to OpenTokenWallet.
   856  type TokenConfig struct {
   857  	// AssetID of the token.
   858  	AssetID uint32
   859  	// Settings correspond to Token.Definition.ConfigOpts.
   860  	Settings map[string]string
   861  	// Emit is a WalletEmitter that manages a channel over which an asset may
   862  	// issue notifications.
   863  	Emit *WalletEmitter
   864  	// PeersChange will be called after the parent's PeersChange.
   865  	PeersChange func(uint32, error)
   866  }
   867  
   868  // TokenMaster is implemented by assets which support degenerate tokens.
   869  type TokenMaster interface {
   870  	// CreateTokenWallet creates a wallet for the specified token asset. The
   871  	// settings correspond to the Token.Definition.ConfigOpts.
   872  	CreateTokenWallet(assetID uint32, settings map[string]string) error
   873  	// OpenTokenWallet opens a wallet for the specified token asset.
   874  	OpenTokenWallet(cfg *TokenConfig) (Wallet, error)
   875  }
   876  
   877  // AccountLocker is a wallet in which redemptions and refunds require a wallet
   878  // to have available balance to pay fees.
   879  type AccountLocker interface {
   880  	// ReserveNRedemption is used when preparing funding for an order that
   881  	// redeems to an account-based asset. The wallet will set aside the
   882  	// appropriate amount of funds so that we can redeem N swaps using the
   883  	// specified fee and asset version. It is an error to request funds >
   884  	// spendable balance.
   885  	ReserveNRedemptions(n uint64, ver uint32, maxFeeRate uint64) (uint64, error)
   886  	// ReReserveRedemption is used when reconstructing existing orders on
   887  	// startup. It is an error to request funds > spendable balance.
   888  	ReReserveRedemption(amt uint64) error
   889  	// UnlockRedemptionReserves is used to return funds reserved for redemption
   890  	// when an order is canceled or otherwise completed unfilled.
   891  	UnlockRedemptionReserves(uint64)
   892  	// ReserveNRefunds is used when preparing funding for an order that refunds
   893  	// to an account-based asset. The wallet will set aside the appropriate
   894  	// amount of funds so that we can refund N swaps using the specified fee and
   895  	// asset version. It is an error to request funds > spendable balance.
   896  	ReserveNRefunds(n uint64, ver uint32, maxFeeRate uint64) (uint64, error)
   897  	// ReReserveRefund is used when reconstructing existing orders on
   898  	// startup. It is an error to request funds > spendable balance.
   899  	ReReserveRefund(uint64) error
   900  	// UnlockRefundReserves is used to return funds reserved for refunds
   901  	// when an order was cancelled or revoked before a swap was initiated,
   902  	// completed successfully, or after a refund was done.
   903  	UnlockRefundReserves(uint64)
   904  }
   905  
   906  // LiveReconfigurer is a wallet that can possibly handle a reconfiguration
   907  // without the need for re-initialization.
   908  type LiveReconfigurer interface {
   909  	// Reconfigure attempts to reconfigure the wallet. If reconfiguration
   910  	// requires a restart, the Wallet should still validate as much
   911  	// configuration as possible.
   912  	Reconfigure(ctx context.Context, cfg *WalletConfig, currentAddress string) (restartRequired bool, err error)
   913  }
   914  
   915  // PeerSource specifies how a wallet knows about a peer. It may have been
   916  // hardcoded into the wallet code, added manually by the user, or discovered
   917  // by communicating with the default/user added peers.
   918  type PeerSource uint16
   919  
   920  const (
   921  	WalletDefault PeerSource = iota
   922  	UserAdded
   923  	Discovered
   924  )
   925  
   926  // WalletPeer provides information about a wallet's peer.
   927  type WalletPeer struct {
   928  	Addr      string     `json:"addr"`
   929  	Source    PeerSource `json:"source"`
   930  	Connected bool       `json:"connected"`
   931  }
   932  
   933  // PeerManager is a wallet which provides allows the user to see the peers the
   934  // wallet is connected to and add new peers.
   935  type PeerManager interface {
   936  	// Peers returns a list of peers that the wallet is connected to.
   937  	Peers() ([]*WalletPeer, error)
   938  	// AddPeer connects the wallet to a new peer. The peer's address will be
   939  	// persisted and connected to each time the wallet is started up.
   940  	AddPeer(addr string) error
   941  	// RemovePeer will remove a peer that was added by AddPeer. This peer may
   942  	// still be connected to by the wallet if it discovers it on its own.
   943  	RemovePeer(addr string) error
   944  }
   945  
   946  type ApprovalStatus uint8
   947  
   948  const (
   949  	Approved ApprovalStatus = iota
   950  	Pending
   951  	NotApproved
   952  )
   953  
   954  // TokenApprover is implemented by wallets that require an approval before
   955  // trading.
   956  type TokenApprover interface {
   957  	// ApproveToken sends an approval transaction for a specific version of
   958  	// the token's swap contract. An error is returned if an approval has
   959  	// already been done or is pending. The onConfirm callback is called
   960  	// when the approval transaction is confirmed.
   961  	ApproveToken(assetVer uint32, onConfirm func()) (string, error)
   962  	// UnapproveToken removes the approval for a specific version of the
   963  	// token's swap contract.
   964  	UnapproveToken(assetVer uint32, onConfirm func()) (string, error)
   965  	// ApprovalStatus returns the approval status for each version of the
   966  	// token's swap contract.
   967  	ApprovalStatus() map[uint32]ApprovalStatus
   968  	// ApprovalFee returns the estimated fee for an approval transaction.
   969  	ApprovalFee(assetVer uint32, approval bool) (uint64, error)
   970  }
   971  
   972  // TicketTransaction represents a ticket transaction.
   973  type TicketTransaction struct {
   974  	Hash        string `json:"hash"`
   975  	TicketPrice uint64 `json:"ticketPrice"`
   976  	Fees        uint64 `json:"fees"`
   977  	Stamp       uint64 `json:"stamp"`
   978  	BlockHeight int64  `json:"blockHeight"`
   979  }
   980  
   981  // TicketStatus from dcrwallet.
   982  type TicketStatus uint
   983  
   984  // Copy of wallet.TicketStatus
   985  const (
   986  	TicketStatusUnknown TicketStatus = iota
   987  	TicketStatusUnmined
   988  	TicketStatusImmature
   989  	TicketStatusLive
   990  	TicketStatusVoted
   991  	TicketStatusMissed
   992  	TicketStatusExpired
   993  	TicketStatusUnspent
   994  	TicketStatusRevoked
   995  )
   996  
   997  // Ticket holds information about a decred ticket.
   998  type Ticket struct {
   999  	Tx      TicketTransaction `json:"tx"`
  1000  	Status  TicketStatus      `json:"status"`
  1001  	Spender string            `json:"spender"`
  1002  }
  1003  
  1004  // TBChoice is a possible agenda choice for a TicketBuyer.
  1005  type TBChoice struct {
  1006  	ID          string `json:"id"`
  1007  	Description string `json:"description"`
  1008  }
  1009  
  1010  // TBAgenda is an agenda that the TicketBuyer can vote on.
  1011  type TBAgenda struct {
  1012  	ID            string      `json:"id"`
  1013  	Description   string      `json:"description"`
  1014  	CurrentChoice string      `json:"currentChoice"`
  1015  	Choices       []*TBChoice `json:"choices"`
  1016  }
  1017  
  1018  // TBTreasurySpend represents a treasury spend that the TicketBuyer can vote on.
  1019  type TBTreasurySpend struct {
  1020  	Hash          string `json:"hash"`
  1021  	Value         uint64 `json:"value"`
  1022  	CurrentPolicy string `json:"currentPolicy"`
  1023  }
  1024  
  1025  // Stances are current policy preferences for the TicketBuyer.
  1026  type Stances struct {
  1027  	Agendas        []*TBAgenda                           `json:"agendas"`
  1028  	TreasurySpends []*TBTreasurySpend                    `json:"tspends"`
  1029  	TreasuryKeys   []*dcrwalletjson.TreasuryPolicyResult `json:"treasuryKeys"`
  1030  }
  1031  
  1032  // VotingServiceProvider is information about a voting service provider.
  1033  type VotingServiceProvider struct {
  1034  	URL           string      `json:"url"`
  1035  	Network       dex.Network `json:"network"`
  1036  	Launched      uint64      `json:"launched"`    // milliseconds
  1037  	LastUpdated   uint64      `json:"lastUpdated"` // milliseconds
  1038  	APIVersions   []int64     `json:"apiVersions"`
  1039  	FeePercentage float64     `json:"feePercentage"`
  1040  	Closed        bool        `json:"closed"`
  1041  	Voting        int64       `json:"voting"`
  1042  	Voted         int64       `json:"voted"`
  1043  	Revoked       int64       `json:"revoked"`
  1044  	VSPDVersion   string      `json:"vspdVersion"`
  1045  	BlockHeight   uint32      `json:"blockHeight"`
  1046  	NetShare      float32     `json:"netShare"`
  1047  }
  1048  
  1049  // TicketStats sums up some statistics for historical staking data for a
  1050  // TicketBuyer.
  1051  type TicketStats struct {
  1052  	TotalRewards uint64 `json:"totalRewards"`
  1053  	TicketCount  uint32 `json:"ticketCount"`
  1054  	Votes        uint32 `json:"votes"`
  1055  	Revokes      uint32 `json:"revokes"`
  1056  	Queued       uint32 `json:"queued"`
  1057  	Mempool      uint32 `json:"mempool"`
  1058  }
  1059  
  1060  // TicketStakingStatus holds various stake information from the wallet.
  1061  type TicketStakingStatus struct {
  1062  	// TicketPrice is the current price of one ticket. Also known as the
  1063  	// stake difficulty.
  1064  	TicketPrice uint64 `json:"ticketPrice"`
  1065  	// VotingSubsidy is the current reward for a vote.
  1066  	VotingSubsidy uint64 `json:"votingSubsidy"`
  1067  	// VSP is the currently set VSP address and fee.
  1068  	VSP string `json:"vsp"`
  1069  	// IsRPC will be true if this is an RPC wallet, in which case we can't
  1070  	// set a new VSP and some other information may not be available.
  1071  	IsRPC bool `json:"isRPC"`
  1072  	// Tickets returns current active tickets up until they are voted or
  1073  	// revoked. Includes unconfirmed tickets.
  1074  	Tickets []*Ticket `json:"tickets"`
  1075  	// Stances returns current voting preferences.
  1076  	Stances Stances `json:"stances"`
  1077  	// Stats is statistical info about staking history.
  1078  	Stats TicketStats `json:"stats"`
  1079  }
  1080  
  1081  // TicketBuyer is a wallet that can participate in decred staking.
  1082  //
  1083  // TODO: Consider adding (*AutoClient).ProcessUnprocessedTickets/ProcessManagedTickets
  1084  // to be used when restoring wallet from seed.
  1085  type TicketBuyer interface {
  1086  	// StakeStatus returns current staking statuses such as currently owned
  1087  	// tickets, ticket price, and current voting preferences.
  1088  	StakeStatus() (*TicketStakingStatus, error)
  1089  	// SetVSP sets the VSP provider.
  1090  	SetVSP(addr string) error
  1091  	// PurchaseTickets starts an aysnchronous process to purchase n tickets.
  1092  	// Look for TicketPurchaseUpdate notifications to track the process.
  1093  	PurchaseTickets(n int, feeSuggestion uint64) error
  1094  	// SetVotingPreferences sets default voting settings for all active
  1095  	// tickets and future tickets. Nil maps can be provided for no change.
  1096  	SetVotingPreferences(choices, tSpendPolicy, treasuryPolicy map[string]string) error
  1097  	// ListVSPs lists known available voting service providers.
  1098  	ListVSPs() ([]*VotingServiceProvider, error)
  1099  	// TicketPage fetches a page of tickets within a range of block numbers with
  1100  	// a target page size and optional offset. scanStart is the block in which
  1101  	// to start the scan. The scan progresses in reverse block number order,
  1102  	// starting at scanStart and going to progressively lower blocks. scanStart
  1103  	// can be set to -1 to indicate the current chain tip.
  1104  	TicketPage(scanStart int32, n, skipN int) ([]*Ticket, error)
  1105  }
  1106  
  1107  // TransactionType is the type of transaction made by a wallet.
  1108  type TransactionType uint16
  1109  
  1110  const (
  1111  	Unknown TransactionType = iota
  1112  	Send
  1113  	Receive
  1114  	Swap
  1115  	Redeem
  1116  	Refund
  1117  	Split
  1118  	CreateBond
  1119  	RedeemBond
  1120  	ApproveToken
  1121  	Acceleration
  1122  	SelfSend
  1123  	RevokeTokenApproval
  1124  	TicketPurchase
  1125  	TicketVote
  1126  	TicketRevocation
  1127  	// SwapOrSend is used when a wallet scanned its historical transactions
  1128  	// and was unable to determine if the transaction was a swap or a send.
  1129  	SwapOrSend
  1130  	Mix
  1131  )
  1132  
  1133  // IncomingTxType returns true if the wallet's balance increases due to a
  1134  // transaction.
  1135  func IncomingTxType(txType TransactionType) bool {
  1136  	return txType == Receive || txType == Redeem || txType == Refund || txType == RedeemBond
  1137  }
  1138  
  1139  // BondTxInfo contains information about a CreateBond or RedeemBond
  1140  // transaction.
  1141  type BondTxInfo struct {
  1142  	// AccountID is the account is the account ID that the bond is applied to.
  1143  	AccountID dex.Bytes `json:"accountID"`
  1144  	// LockTime is the time until which the bond is locked.
  1145  	LockTime uint64 `json:"lockTime"`
  1146  	// BondID is the ID of the bond.
  1147  	BondID dex.Bytes `json:"bondID"`
  1148  }
  1149  
  1150  // WalletTransaction represents a transaction that was made by a wallet.
  1151  type WalletTransaction struct {
  1152  	Type   TransactionType `json:"type"`
  1153  	ID     string          `json:"id"`
  1154  	Amount uint64          `json:"amount"`
  1155  	Fees   uint64          `json:"fees"`
  1156  	// BlockNumber is 0 for txs in the mempool.
  1157  	BlockNumber uint64 `json:"blockNumber"`
  1158  	// Timestamp is the time the transaction was mined.
  1159  	Timestamp uint64 `json:"timestamp"`
  1160  	// TokenID will be non-nil if the BalanceDelta applies to the balance
  1161  	// of a token.
  1162  	TokenID *uint32 `json:"tokenID,omitempty"`
  1163  	// Recipient will be non-nil for Send/Receive transactions, and specifies the
  1164  	// recipient address of the transaction.
  1165  	Recipient *string `json:"recipient,omitempty"`
  1166  	// BondInfo will be non-nil for CreateBond and RedeemBond transactions.
  1167  	BondInfo *BondTxInfo `json:"bondInfo,omitempty"`
  1168  	// AdditionalData contains asset specific information, i.e. nonce
  1169  	// for ETH.
  1170  	AdditionalData map[string]string `json:"additionalData"`
  1171  	// Confirmed is true when the transaction is considered finalized.
  1172  	// Confirmed transactions are no longer updated and will be considered
  1173  	// finalized forever.
  1174  	Confirmed bool `json:"confirmed"`
  1175  	// Rejected will be true the transaction was rejected and did not have any
  1176  	// effect, though fees were incurred.
  1177  	Rejected bool `json:"rejected,omitempty"`
  1178  }
  1179  
  1180  // WalletHistorian is a wallet that is able to retrieve the history of all
  1181  // transactions it has made.
  1182  type WalletHistorian interface {
  1183  	// TxHistory returns all the transactions a wallet has made. If refID
  1184  	// is nil, then transactions starting from the most recent are returned
  1185  	// (past is ignored). If past is true, the transactions prior to the
  1186  	// refID are returned, otherwise the transactions after the refID are
  1187  	// returned. n is the number of transactions to return. If n is <= 0,
  1188  	// all the transactions will be returned.
  1189  	TxHistory(n int, refID *string, past bool) ([]*WalletTransaction, error)
  1190  	// WalletTransaction returns a single transaction that either a wallet
  1191  	// has made or in which the wallet has received funds. This function may
  1192  	// support more transactions than are returned by TxHistory. For example,
  1193  	// ETH/token wallets do not return receiving transactions in TxHistory,
  1194  	// but WalletTransaction will return them.
  1195  	WalletTransaction(ctx context.Context, txID string) (*WalletTransaction, error)
  1196  }
  1197  
  1198  // Bond is the fidelity bond info generated for a certain account ID, amount,
  1199  // and lock time. These data are intended for the "post bond" request, in which
  1200  // the server pre-validates the unsigned transaction, the client then publishes
  1201  // the corresponding signed transaction, and a final request is made once the
  1202  // bond is fully confirmed. The caller should manage the private key.
  1203  type Bond struct {
  1204  	Version uint16
  1205  	AssetID uint32
  1206  	Amount  uint64
  1207  	CoinID  []byte
  1208  	Data    []byte // additional data to interpret the bond e.g. redeem script, bond contract, etc.
  1209  	// SignedTx and UnsignedTx are the opaque (raw bytes) signed and unsigned
  1210  	// bond creation transactions, in whatever encoding and funding scheme for
  1211  	// this asset and wallet. The unsigned one is used to pre-validate this bond
  1212  	// with the server prior to publishing it, thus locking funds for a long
  1213  	// period of time. Once the bond is pre-validated, the signed tx may then be
  1214  	// published by the wallet.
  1215  	SignedTx, UnsignedTx []byte
  1216  	// RedeemTx is a backup transaction that spends the bond output. Normally
  1217  	// the a key index will be used to derive the key when the bond expires.
  1218  	RedeemTx []byte
  1219  }
  1220  
  1221  // Balance is categorized information about a wallet's balance.
  1222  type Balance struct {
  1223  	// Available is the balance that is available for trading immediately.
  1224  	Available uint64 `json:"available"`
  1225  	// Immature is the balance that is not ready, but will be after some
  1226  	// confirmations.
  1227  	Immature uint64 `json:"immature"`
  1228  	// Locked is the total amount locked in the wallet which includes but
  1229  	// is not limited to funds locked for swap but not actually swapped yet.
  1230  	Locked uint64 `json:"locked"`
  1231  	// BondReserves is the amount of funds locked in the wallet for expenses
  1232  	// associated with bond maintenance.
  1233  	BondReserves uint64 `json:"bondReserves"`
  1234  	// ReservesDeficit is the difference between the available balance and the
  1235  	// amount reserved for specific purposes.
  1236  	ReservesDeficit uint64 `json:"reservesDeficit"`
  1237  	// Other is a place to list custom balance categories. It is recommended for
  1238  	// custom balance added here to have a translation and tooltip info in
  1239  	// client/webserver/site/src/js/wallet.js#customWalletBalanceCategory
  1240  	Other map[BalanceCategory]CustomBalance `json:"other"`
  1241  }
  1242  
  1243  // CustomBalance is a balance category used to track funds for a particular
  1244  // purpose or for a special kind of balance (e.g Zcash Shielded wallet
  1245  // balances).
  1246  type CustomBalance struct {
  1247  	// Amount is the balance in the wallet for this custom category. It is
  1248  	// subtracted from Balance.Available above.
  1249  	Amount uint64 `json:"amt"`
  1250  	// Locked is a flag to indicate that this Amount is not included in
  1251  	// Balance.Available and is included in Balance.Locked.
  1252  	Locked bool `json:"locked"`
  1253  }
  1254  
  1255  // BalanceCategory is a string identifier for a custom balance category.
  1256  type BalanceCategory string
  1257  
  1258  // Balance categories for custom balances These values are used as a map key for
  1259  // custom balances and may be recognized in the frontend to support translation.
  1260  // It is recommended for custom balance categories listed here to have a
  1261  // translation and tooltip info in
  1262  // client/webserver/site/src/js/wallet.js#customWalletBalanceCategory. If any of
  1263  // these balance categories should change, the customWalletBalanceCategory
  1264  // function in the wallet.js file above should be updated with the new value.
  1265  const (
  1266  	BalanceCategoryShielded = "Shielded"
  1267  	BalanceCategoryUnmixed  = "Unmixed"
  1268  	BalanceCategoryStaked   = "Staked"
  1269  )
  1270  
  1271  // Coin is some amount of spendable asset. Coin provides the information needed
  1272  // to locate the unspent value on the blockchain.
  1273  type Coin interface {
  1274  	// ID is a unique identifier for this coin.
  1275  	ID() dex.Bytes
  1276  	// String is a string representation of the coin.
  1277  	String() string
  1278  	// Value is the available quantity, in atoms/satoshi.
  1279  	Value() uint64
  1280  	// TxID is the ID of the transaction that created the coin.
  1281  	TxID() string
  1282  }
  1283  
  1284  // TokenCoin extends the Coin interface to include the amount locked
  1285  // of the parent asset to be used for fees.
  1286  type TokenCoin interface {
  1287  	Coin
  1288  	Fees() uint64
  1289  }
  1290  
  1291  type RecoveryCoin interface {
  1292  	// RecoveryID is an ID that can be used to re-establish funding state during
  1293  	// startup. If a Coin implements RecoveryCoin, the RecoveryID will be used
  1294  	// in the database record, and ultimately passed to the FundingCoins method.
  1295  	RecoveryID() dex.Bytes
  1296  }
  1297  
  1298  // Coins a collection of coins as returned by Fund.
  1299  type Coins []Coin
  1300  
  1301  // Receipt holds information about a sent swap contract.
  1302  type Receipt interface {
  1303  	// Expiration is the time lock expiration.
  1304  	Expiration() time.Time
  1305  	// Coin is the swap initiation transaction's Coin.
  1306  	Coin() Coin
  1307  	// Contract is the unique swap contract data. This may be a redeem script
  1308  	// for UTXO assets, or other information that uniquely identifies the swap
  1309  	// for account-based assets e.g. a contract version and secret hash for ETH.
  1310  	Contract() dex.Bytes
  1311  	// String provides a human-readable representation of the swap that may
  1312  	// provide supplementary data to locate the swap.
  1313  	String() string
  1314  	// SignedRefund is a signed refund script that can be used to return
  1315  	// funds to the user in the case a contract expires.
  1316  	SignedRefund() dex.Bytes
  1317  }
  1318  
  1319  // AuditInfo is audit information about a swap contract needed to audit the
  1320  // contract.
  1321  type AuditInfo struct {
  1322  	// Recipient is the string-encoded recipient address.
  1323  	Recipient string
  1324  	// Expiration is the unix timestamp of the contract time lock expiration.
  1325  	Expiration time.Time
  1326  	// Coin is the coin that contains the contract.
  1327  	Coin Coin
  1328  	// Contract is the unique swap contract data. This may be a redeem script
  1329  	// for UTXO assets, or other information that uniquely identifies the swap
  1330  	// for account-based assets e.g. a contract version and secret hash for ETH.
  1331  	Contract dex.Bytes
  1332  	// SecretHash is the contract's secret hash. This is likely to be encoded in
  1333  	// the Contract field, which is often the redeem script or an asset-specific
  1334  	// encoding of the unique swap data.
  1335  	SecretHash dex.Bytes
  1336  }
  1337  
  1338  // INPUT TYPES
  1339  // The types below will be used by the client as inputs for the methods exposed
  1340  // by the wallet.
  1341  
  1342  // Swaps is the details needed to broadcast a swap contract(s).
  1343  type Swaps struct {
  1344  	// Version is the asset version.
  1345  	Version uint32
  1346  	// Inputs are the Coins being spent.
  1347  	Inputs Coins
  1348  	// Contract is the contract data.
  1349  	Contracts []*Contract
  1350  	// FeeRate is the required fee rate in atoms/byte.
  1351  	FeeRate uint64
  1352  	// LockChange can be set to true if the change should be locked for
  1353  	// subsequent matches.
  1354  	LockChange bool
  1355  	// Options are OrderOptions set or selected by the user at order time.
  1356  	Options map[string]string
  1357  }
  1358  
  1359  // Contract is a swap contract.
  1360  type Contract struct {
  1361  	// Address is the receiving address.
  1362  	Address string
  1363  	// Value is the amount being traded.
  1364  	Value uint64
  1365  	// SecretHash is the hash of the secret key.
  1366  	SecretHash dex.Bytes
  1367  	// LockTime is the contract lock time in UNIX seconds.
  1368  	LockTime uint64
  1369  }
  1370  
  1371  // Redemption is a redemption transaction that spends a counter-party's swap
  1372  // contract.
  1373  type Redemption struct {
  1374  	// Spends is the AuditInfo for the swap output being spent.
  1375  	Spends *AuditInfo
  1376  	// Secret is the secret key needed to satisfy the swap contract.
  1377  	Secret dex.Bytes
  1378  }
  1379  
  1380  // RedeemForm is a group of Redemptions. The struct will be
  1381  // expanded in in-progress work to accommodate order-time options.
  1382  type RedeemForm struct {
  1383  	Redemptions []*Redemption
  1384  	// FeeSuggestion is a suggested fee rate. For redemptions, the suggestion is
  1385  	// just a fallback if an internal estimate using the wallet's redeem confirm
  1386  	// block target setting is not available. Since this is the redemption,
  1387  	// there is no obligation on the client to use the fee suggestion in any
  1388  	// way, but obviously fees that are too low may result in the redemption
  1389  	// getting stuck in mempool.
  1390  	FeeSuggestion uint64
  1391  	Options       map[string]string
  1392  }
  1393  
  1394  // Order is order details needed for FundOrder.
  1395  type Order struct {
  1396  	// Version is the asset version of the "from" asset with the init
  1397  	// transaction.
  1398  	Version uint32
  1399  	// Value is the amount required to satisfy the order. The Value does not
  1400  	// include fees. Fees will be calculated internally based on the number of
  1401  	// possible swaps (MaxSwapCount) and the exchange's configuration
  1402  	// (Exchange).
  1403  	Value uint64
  1404  	// MaxSwapCount is the number of lots in the order, which is also the
  1405  	// maximum number of transactions that an order could potentially generate
  1406  	// in a worst-case scenario of all 1-lot matches.
  1407  	MaxSwapCount uint64 // uint64 for compatibility with quantity and lot size.
  1408  	// MaxFeeRate is the largest possible fee rate for the init transaction (of
  1409  	// this "from" asset) specific to and provided by a particular server, and
  1410  	// is used to calculate the funding required to cover fees.
  1411  	MaxFeeRate uint64
  1412  	// Immediate should be set to true if this is for an order that is not a
  1413  	// standing order, likely a market order or a limit order with immediate
  1414  	// time-in-force.
  1415  	Immediate bool
  1416  	// FeeSuggestion is a suggested fee from the server. If a split transaction
  1417  	// is used, the fee rate used should be at least the suggested fee, else
  1418  	// zero-conf coins might be rejected.
  1419  	FeeSuggestion uint64
  1420  	// Options are options that corresponds to PreSwap.Options, as well as
  1421  	// their values.
  1422  	Options map[string]string
  1423  
  1424  	// The following fields are only used for some assets where the redeemed/to
  1425  	// asset may require funds in this "from" asset. For example, buying ERC20
  1426  	// tokens with ETH.
  1427  
  1428  	// RedeemVersion is the asset version of the "to" asset with the redeem
  1429  	// transaction.
  1430  	RedeemVersion uint32
  1431  	// RedeemAssetID is the asset ID of the "to" asset.
  1432  	RedeemAssetID uint32
  1433  }
  1434  
  1435  // MultiOrderValue is one of the placements in a multi-order.
  1436  type MultiOrderValue struct {
  1437  	// Value is the amount required to satisfy the order. The Value does not
  1438  	// include fees. Fees will be calculated internally based on the number of
  1439  	// possible swaps (MaxSwapCount) and the exchange's configuration
  1440  	// (Exchange).
  1441  	Value uint64
  1442  	// MaxSwapCount is the number of lots in the order, which is also the
  1443  	// maximum number of transactions that an order could potentially generate
  1444  	// in a worst-case scenario of all 1-lot matches.
  1445  	MaxSwapCount uint64 // uint64 for compatibility with quantity and lot size.
  1446  }
  1447  
  1448  // MultiOrder is order details needed for FundMultiOrder.
  1449  type MultiOrder struct {
  1450  	// Version is the asset version of the "from" asset with the init
  1451  	// transaction.
  1452  	Version uint32
  1453  	Values  []*MultiOrderValue
  1454  	// MaxFeeRate is the largest possible fee rate for the init transaction (of
  1455  	// this "from" asset) specific to and provided by a particular server, and
  1456  	// is used to calculate the funding required to cover fees.
  1457  	MaxFeeRate uint64
  1458  	// FeeSuggestion is a suggested fee from the server. If a split transaction
  1459  	// is used, the fee rate used should be at least the suggested fee, else
  1460  	// zero-conf coins might be rejected.
  1461  	FeeSuggestion uint64
  1462  	// Options are options that corresponds to PreSwap.Options, as well as
  1463  	// their values.
  1464  	Options map[string]string
  1465  
  1466  	// The following fields are only used for some assets where the redeemed/to
  1467  	// asset may require funds in this "from" asset. For example, buying ERC20
  1468  	// tokens with ETH.
  1469  
  1470  	// RedeemVersion is the asset version of the "to" asset with the redeem
  1471  	// transaction.
  1472  	RedeemVersion uint32
  1473  	// RedeemAssetID is the asset ID of the "to" asset.
  1474  	RedeemAssetID uint32
  1475  }
  1476  
  1477  // A GeocodeRedeemer redeems funds from a geocode game.
  1478  type GeocodeRedeemer interface {
  1479  	RedeemGeocode(code []byte, msg string) (dex.Bytes, uint64, error)
  1480  }
  1481  
  1482  // WalletNotification can be any asynchronous information the wallet needs
  1483  // to convey.
  1484  type WalletNotification any
  1485  
  1486  type baseWalletNotification struct {
  1487  	AssetID uint32 `json:"assetID"`
  1488  	Route   string `json:"route"`
  1489  }
  1490  
  1491  // TipChangeNote is the only required wallet notification. All wallets should
  1492  // emit a TipChangeNote when a state change occurs that might necessitate swap
  1493  // progression or new balance checks.
  1494  type TipChangeNote struct {
  1495  	baseWalletNotification
  1496  	Tip  uint64 `json:"tip"`
  1497  	Data any    `json:"data"`
  1498  }
  1499  
  1500  // BalanceChangeNote can be sent when the wallet detects a balance change
  1501  // between tip changes.
  1502  type BalanceChangeNote struct {
  1503  	baseWalletNotification
  1504  	Balance *Balance
  1505  }
  1506  
  1507  // TransactionNote is sent when a transaction is made, seen, or updated.
  1508  type TransactionNote struct {
  1509  	baseWalletNotification
  1510  	Transaction *WalletTransaction `json:"transaction"`
  1511  	New         bool               `json:"new"`
  1512  }
  1513  
  1514  // CustomWalletNote is any other information the wallet wishes to convey to
  1515  // the user.
  1516  type CustomWalletNote struct {
  1517  	baseWalletNotification
  1518  	Payload any `json:"payload"`
  1519  }
  1520  
  1521  type ActionTaker interface {
  1522  	// TakeAction processes a response to an ActionRequired wallet notification.
  1523  	TakeAction(actionID string, payload []byte) error
  1524  }
  1525  
  1526  // WalletEmitter handles a channel for wallet notifications and provides methods
  1527  // that generates notifications.
  1528  type WalletEmitter struct {
  1529  	c       chan<- WalletNotification
  1530  	assetID uint32
  1531  	log     dex.Logger
  1532  }
  1533  
  1534  type ActionRequiredNote struct {
  1535  	baseWalletNotification
  1536  	Payload  any    `json:"payload"`
  1537  	UniqueID string `json:"uniqueID"`
  1538  	ActionID string `json:"actionID"`
  1539  }
  1540  
  1541  type ActionResolvedNote struct {
  1542  	baseWalletNotification
  1543  	UniqueID string `json:"uniqueID"`
  1544  }
  1545  
  1546  // NewWalletEmitter constructs a WalletEmitter for an asset.
  1547  func NewWalletEmitter(c chan<- WalletNotification, assetID uint32, log dex.Logger) *WalletEmitter {
  1548  	return &WalletEmitter{
  1549  		c:       c,
  1550  		assetID: assetID,
  1551  		log:     log,
  1552  	}
  1553  }
  1554  
  1555  func (e *WalletEmitter) emit(note WalletNotification) {
  1556  	select {
  1557  	case e.c <- note:
  1558  	default:
  1559  		e.log.Warn("blocking WalletNotification channel")
  1560  	}
  1561  }
  1562  
  1563  // Data sends a CustomWalletNote with the specified data payload.
  1564  func (e *WalletEmitter) Data(route string, payload any) {
  1565  	e.emit(&CustomWalletNote{
  1566  		baseWalletNotification: baseWalletNotification{
  1567  			AssetID: e.assetID,
  1568  			Route:   route,
  1569  		}, Payload: payload,
  1570  	})
  1571  }
  1572  
  1573  // TipChange sends a TipChangeNote with optional extra data.
  1574  func (e *WalletEmitter) TipChange(tip uint64, datas ...any) {
  1575  	var data any
  1576  	if len(datas) > 0 {
  1577  		data = datas[0]
  1578  	}
  1579  	e.emit(&TipChangeNote{
  1580  		baseWalletNotification: baseWalletNotification{
  1581  			AssetID: e.assetID,
  1582  			Route:   "tipChange",
  1583  		},
  1584  		Tip: tip, Data: data,
  1585  	})
  1586  }
  1587  
  1588  // BalanceChange sends a BalanceChangeNote.
  1589  func (e *WalletEmitter) BalanceChange(bal *Balance) {
  1590  	e.emit(&BalanceChangeNote{
  1591  		baseWalletNotification: baseWalletNotification{
  1592  			AssetID: e.assetID,
  1593  			Route:   "balanceChange",
  1594  		},
  1595  		Balance: bal,
  1596  	})
  1597  }
  1598  
  1599  // TransactionNote sends a TransactionNote.
  1600  func (e *WalletEmitter) TransactionNote(tx *WalletTransaction, new bool) {
  1601  	e.emit(&TransactionNote{
  1602  		baseWalletNotification: baseWalletNotification{
  1603  			AssetID: e.assetID,
  1604  			Route:   "transaction",
  1605  		},
  1606  		Transaction: tx,
  1607  		New:         new,
  1608  	})
  1609  }
  1610  
  1611  // TransactionHistorySyncedNote sends a TransactionHistorySyncedNote.
  1612  func (e *WalletEmitter) TransactionHistorySyncedNote() {
  1613  	e.emit(&baseWalletNotification{
  1614  		AssetID: e.assetID,
  1615  		Route:   "transactionHistorySynced",
  1616  	})
  1617  }
  1618  
  1619  // ActionRequired is a route that will end up as a special dialogue seeking
  1620  // user input via (ActionTaker).TakeAction.
  1621  func (e *WalletEmitter) ActionRequired(uniqueID, actionID string, payload any) {
  1622  	e.emit(&ActionRequiredNote{
  1623  		baseWalletNotification: baseWalletNotification{
  1624  			AssetID: e.assetID,
  1625  			Route:   "actionRequired",
  1626  		},
  1627  		UniqueID: uniqueID,
  1628  		ActionID: actionID,
  1629  		Payload:  payload,
  1630  	})
  1631  }
  1632  
  1633  func (e *WalletEmitter) ActionResolved(uniqueID string) {
  1634  	e.emit(&ActionResolvedNote{
  1635  		baseWalletNotification: baseWalletNotification{
  1636  			AssetID: e.assetID,
  1637  			Route:   "actionResolved",
  1638  		},
  1639  		UniqueID: uniqueID,
  1640  	})
  1641  }