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