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 }