decred.org/dcrdex@v1.0.5/client/rpcserver/types.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 rpcserver 5 6 import ( 7 "encoding/hex" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "math" 12 "strconv" 13 "time" 14 15 "decred.org/dcrdex/client/core" 16 "decred.org/dcrdex/client/mm" 17 "decred.org/dcrdex/dex" 18 "decred.org/dcrdex/dex/config" 19 "decred.org/dcrdex/dex/encode" 20 "decred.org/dcrdex/dex/order" 21 ) 22 23 // An orderID is a 256 bit number encoded as a hex string. 24 const orderIdLen = 2 * order.OrderIDSize // 2 * 32 25 26 var ( 27 // errArgs is wrapped when arguments to the known command cannot be parsed. 28 errArgs = errors.New("unable to parse arguments") 29 ) 30 31 // RawParams is used for all server requests. 32 type RawParams struct { 33 PWArgs []encode.PassBytes `json:"PWArgs"` 34 Args []string `json:"args"` 35 } 36 37 // VersionResponse holds bisonw and bisonw rpc server version. 38 type VersionResponse struct { 39 RPCServerVer *dex.Semver `json:"rpcServerVersion"` 40 BWVersion *SemVersion `json:"dexcVersion"` 41 } 42 43 // SemVersion holds a semver version JSON object. 44 type SemVersion struct { 45 VersionString string `json:"versionString"` 46 Major uint32 `json:"major"` 47 Minor uint32 `json:"minor"` 48 Patch uint32 `json:"patch"` 49 Prerelease string `json:"prerelease,omitempty"` 50 BuildMetadata string `json:"buildMetadata,omitempty"` 51 } 52 53 // getBondAssetsResponse is the getbondassets response payload. 54 type getBondAssetsResponse struct { 55 Expiry uint64 `json:"expiry"` 56 Assets map[string]*core.BondAsset `json:"assets"` 57 } 58 59 // tradeResponse is used when responding to the trade route. 60 type tradeResponse struct { 61 OrderID string `json:"orderID"` 62 Sig string `json:"sig"` 63 Stamp uint64 `json:"stamp"` 64 Error error `json:"error,omitempty"` 65 } 66 67 // myOrdersResponse is used when responding to the myorders route. 68 type myOrdersResponse []*myOrder 69 70 // myOrder represents an order when responding to the myorders route. 71 type myOrder struct { 72 Host string `json:"host"` 73 MarketName string `json:"marketName"` 74 BaseID uint32 `json:"baseID"` 75 QuoteID uint32 `json:"quoteID"` 76 ID string `json:"id"` // Can be empty if part of an InFlightOrder. 77 Type string `json:"type"` 78 Sell bool `json:"sell"` 79 Stamp uint64 `json:"stamp"` 80 SubmitTime uint64 `json:"submitTime"` 81 Age string `json:"age"` 82 Rate uint64 `json:"rate,omitempty"` 83 Quantity uint64 `json:"quantity"` 84 Filled uint64 `json:"filled"` 85 Settled uint64 `json:"settled"` 86 Status string `json:"status"` 87 Cancelling bool `json:"cancelling,omitempty"` 88 Canceled bool `json:"canceled,omitempty"` 89 TimeInForce string `json:"tif,omitempty"` 90 Matches []*match `json:"matches,omitempty"` 91 } 92 93 // match represents a match on an order. An order may have many matches. 94 type match struct { 95 MatchID string `json:"matchID"` 96 Status string `json:"status"` 97 Revoked bool `json:"revoked"` 98 Rate uint64 `json:"rate"` 99 Qty uint64 `json:"qty"` 100 Side string `json:"side"` 101 FeeRate uint64 `json:"feeRate"` 102 Swap string `json:"swap,omitempty"` 103 CounterSwap string `json:"counterSwap,omitempty"` 104 Redeem string `json:"redeem,omitempty"` 105 CounterRedeem string `json:"counterRedeem,omitempty"` 106 Refund string `json:"refund,omitempty"` 107 Stamp uint64 `json:"stamp"` 108 IsCancel bool `json:"isCancel"` 109 } 110 111 // discoverAcctForm is information necessary to discover an account used with a 112 // certain dex. 113 type discoverAcctForm struct { 114 addr string 115 appPass encode.PassBytes 116 cert any 117 } 118 119 // openWalletForm is information necessary to open a wallet. 120 type openWalletForm struct { 121 assetID uint32 122 appPass encode.PassBytes 123 } 124 125 // walletStatusForm is information necessary to change a wallet's status. 126 type walletStatusForm struct { 127 assetID uint32 128 disable bool 129 } 130 131 // newWalletForm is information necessary to create a new wallet. 132 type newWalletForm struct { 133 assetID uint32 134 walletType string 135 config map[string]string 136 walletPass encode.PassBytes 137 appPass encode.PassBytes 138 } 139 140 // helpForm is information necessary to obtain help. 141 type helpForm struct { 142 helpWith string 143 includePasswords bool 144 } 145 146 // tradeForm combines the application password and the user's trade details. 147 type tradeForm struct { 148 appPass encode.PassBytes 149 srvForm *core.TradeForm 150 } 151 152 // multiTradeForm combines the application password and the user's trade 153 // details. 154 type multiTradeForm struct { 155 appPass encode.PassBytes 156 srvForm *core.MultiTradeForm 157 } 158 159 // cancelForm is information necessary to cancel a trade. 160 type cancelForm struct { 161 orderID dex.Bytes 162 } 163 164 // sendOrWithdrawForm is information necessary to send or withdraw funds. 165 type sendOrWithdrawForm struct { 166 appPass encode.PassBytes 167 assetID uint32 168 value uint64 169 address string 170 } 171 172 // orderBookForm is information necessary to fetch an order book. 173 type orderBookForm struct { 174 host string 175 base uint32 176 quote uint32 177 nOrders uint64 178 } 179 180 // myOrdersForm is information necessary to fetch the user's orders. 181 type myOrdersForm struct { 182 host string 183 base *uint32 184 quote *uint32 185 } 186 187 type deleteRecordsForm struct { 188 olderThan *time.Time 189 ordersFileStr, matchesFileStr string 190 } 191 192 // addRemovePeerForm is the information necessary to add or remove a wallet peer. 193 type addRemovePeerForm struct { 194 assetID uint32 195 address string 196 } 197 198 type mmAvailableBalancesForm struct { 199 cfgFilePath string 200 mkt *mm.MarketWithHost 201 } 202 203 type startBotForm struct { 204 appPass encode.PassBytes 205 cfgFilePath string 206 mkt *mm.MarketWithHost 207 } 208 209 type updateRunningBotForm struct { 210 cfgFilePath string 211 mkt *mm.MarketWithHost 212 balances *mm.BotInventoryDiffs 213 } 214 215 type updateRunningBotInventoryForm struct { 216 mkt *mm.MarketWithHost 217 balances *mm.BotInventoryDiffs 218 } 219 220 type setVSPForm struct { 221 assetID uint32 222 addr string 223 } 224 225 type purchaseTicketsForm struct { 226 assetID uint32 227 num int 228 appPass encode.PassBytes 229 } 230 231 type setVotingPreferencesForm struct { 232 assetID uint32 233 voteChoices, tSpendPolicy, treasuryPolicy map[string]string 234 } 235 236 type txHistoryForm struct { 237 assetID uint32 238 num int 239 refID *string 240 past bool 241 } 242 243 // checkNArgs checks that args and pwArgs are the correct length. 244 func checkNArgs(params *RawParams, nPWArgs, nArgs []int) error { 245 // For want, one integer indicates an exact match, two are the min and max. 246 check := func(have int, want []int) error { 247 if len(want) == 1 { 248 if want[0] != have { 249 return fmt.Errorf("%w: wanted %d argument but got %d argument", errArgs, want[0], have) 250 } 251 } else { 252 if have < want[0] || have > want[1] { 253 return fmt.Errorf("%w: wanted between %d and %d argument but got %d arguments", errArgs, want[0], want[1], have) 254 } 255 } 256 return nil 257 } 258 if err := check(len(params.Args), nArgs); err != nil { 259 return fmt.Errorf("arguments: %w", err) 260 } 261 if err := check(len(params.PWArgs), nPWArgs); err != nil { 262 return fmt.Errorf("password arguments: %w", err) 263 } 264 return nil 265 } 266 267 func checkIntArg(arg, name string, bitSize int) (int64, error) { 268 i, err := strconv.ParseInt(arg, 10, bitSize) 269 if err != nil { 270 return i, fmt.Errorf("%w: cannot parse %s: %v", errArgs, name, err) 271 } 272 return i, nil 273 } 274 275 func checkUIntArg(arg, name string, bitSize int) (uint64, error) { 276 i, err := strconv.ParseUint(arg, 10, bitSize) 277 if err != nil { 278 return i, fmt.Errorf("%w: cannot parse %s: %v", errArgs, name, err) 279 } 280 return i, nil 281 } 282 283 func checkBoolArg(arg, name string) (bool, error) { 284 b, err := strconv.ParseBool(arg) 285 if err != nil { 286 return b, fmt.Errorf("%w: %s must be a boolean: %v", errArgs, name, err) 287 } 288 return b, nil 289 } 290 291 func checkMapArg(arg, name string) (map[string]string, error) { 292 m := make(map[string]string) 293 err := json.Unmarshal([]byte(arg), &m) 294 if err != nil { 295 return nil, fmt.Errorf("%w: %s must be a JSON-encoded map[string]string: %v", errArgs, name, err) 296 } 297 return m, nil 298 } 299 300 func parseDiscoverAcctArgs(params *RawParams) (*discoverAcctForm, error) { 301 if err := checkNArgs(params, []int{1}, []int{1, 2}); err != nil { 302 return nil, err 303 } 304 var cert []byte 305 if len(params.Args) > 1 { 306 cert = []byte(params.Args[1]) 307 } 308 req := &discoverAcctForm{ 309 appPass: params.PWArgs[0], 310 addr: params.Args[0], 311 cert: cert, 312 } 313 return req, nil 314 } 315 316 func parseHelpArgs(params *RawParams) (*helpForm, error) { 317 if err := checkNArgs(params, []int{0}, []int{0, 2}); err != nil { 318 return nil, err 319 } 320 var helpWith string 321 if len(params.Args) > 0 { 322 helpWith = params.Args[0] 323 } 324 var includePasswords bool 325 if len(params.Args) > 1 { 326 var err error 327 includePasswords, err = checkBoolArg(params.Args[1], "includepasswords") 328 if err != nil { 329 return nil, err 330 } 331 } 332 return &helpForm{ 333 helpWith: helpWith, 334 includePasswords: includePasswords, 335 }, nil 336 } 337 338 func parseInitArgs(params *RawParams) (encode.PassBytes, *string, error) { 339 if err := checkNArgs(params, []int{1}, []int{0, 1}); err != nil { 340 return nil, nil, err 341 } 342 if len(params.PWArgs[0]) == 0 { 343 return nil, nil, fmt.Errorf("app password cannot be empty") 344 } 345 var seed *string 346 if len(params.Args) == 1 { 347 seed = ¶ms.Args[0] 348 } 349 return params.PWArgs[0], seed, nil 350 } 351 352 func parseLoginArgs(params *RawParams) (encode.PassBytes, error) { 353 if err := checkNArgs(params, []int{1}, []int{0}); err != nil { 354 return nil, err 355 } 356 return params.PWArgs[0], nil 357 } 358 359 func parseNewWalletArgs(params *RawParams) (*newWalletForm, error) { 360 if err := checkNArgs(params, []int{2}, []int{2, 4}); err != nil { 361 return nil, err 362 } 363 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 364 if err != nil { 365 return nil, err 366 } 367 368 req := &newWalletForm{ 369 appPass: params.PWArgs[0], 370 walletType: params.Args[1], 371 walletPass: params.PWArgs[1], 372 assetID: uint32(assetID), 373 config: make(map[string]string), 374 } 375 if len(params.Args) > 2 { 376 req.config, err = config.Parse([]byte(params.Args[2])) 377 if err != nil { 378 return nil, fmt.Errorf("config parse error: %v", err) 379 } 380 } 381 if len(params.Args) > 3 { 382 cfg := make(map[string]string) 383 err := json.Unmarshal([]byte(params.Args[3]), &cfg) 384 if err != nil { 385 return nil, fmt.Errorf("JSON parse error: %v", err) 386 } 387 for key, val := range cfg { 388 if fileVal, found := req.config[key]; found { 389 log.Infof("Overriding config file setting %s=%s with %s", key, fileVal, val) 390 } 391 req.config[key] = val 392 } 393 } 394 return req, nil 395 } 396 397 func parseOpenWalletArgs(params *RawParams) (*openWalletForm, error) { 398 if err := checkNArgs(params, []int{1}, []int{1}); err != nil { 399 return nil, err 400 } 401 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 402 if err != nil { 403 return nil, err 404 } 405 req := &openWalletForm{appPass: params.PWArgs[0], assetID: uint32(assetID)} 406 return req, nil 407 } 408 409 func parseCloseWalletArgs(params *RawParams) (uint32, error) { 410 if err := checkNArgs(params, []int{0}, []int{1}); err != nil { 411 return 0, err 412 } 413 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 414 if err != nil { 415 return 0, err 416 } 417 return uint32(assetID), nil 418 } 419 420 func parseToggleWalletStatusArgs(params *RawParams) (*walletStatusForm, error) { 421 if err := checkNArgs(params, []int{0}, []int{2}); err != nil { 422 return nil, err 423 } 424 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 425 if err != nil { 426 return nil, err 427 } 428 disable, err := checkBoolArg(params.Args[1], "disable") 429 if err != nil { 430 return nil, err 431 } 432 req := &walletStatusForm{assetID: uint32(assetID), disable: disable} 433 return req, nil 434 } 435 436 func parseGetDEXConfigArgs(params *RawParams) (host string, cert []byte, err error) { 437 if err := checkNArgs(params, []int{0}, []int{1, 2}); err != nil { 438 return "", nil, err 439 } 440 if len(params.Args) == 1 { 441 return params.Args[0], nil, nil 442 } 443 return params.Args[0], []byte(params.Args[1]), nil 444 } 445 446 func parseBondAssetsArgs(params *RawParams) (host string, cert []byte, err error) { 447 if err := checkNArgs(params, []int{0}, []int{1, 2}); err != nil { 448 return "", nil, err 449 } 450 if len(params.Args) == 1 { 451 return params.Args[0], nil, nil 452 } 453 return params.Args[0], []byte(params.Args[1]), nil 454 } 455 456 // bondopts 127.0.0.1:17273 2 2012345678 42 457 func parseBondOptsArgs(params *RawParams) (*core.BondOptionsForm, error) { 458 if err := checkNArgs(params, []int{0}, []int{2, 5}); err != nil { 459 return nil, err 460 } 461 462 var targetTierP *uint64 463 targetTier, err := checkIntArg(params.Args[1], "targetTier", 17) 464 if err != nil { 465 return nil, err 466 } 467 if targetTier >= 0 { 468 targetTierU64 := uint64(targetTier) 469 targetTierP = &targetTierU64 470 } 471 472 var maxBondedP *uint64 473 if len(params.Args) > 2 { 474 maxBonded, err := checkIntArg(params.Args[2], "maxBonded", 64) 475 if err != nil { 476 return nil, err 477 } 478 if maxBonded >= 0 { 479 maxBondedU64 := uint64(maxBonded) 480 maxBondedP = &maxBondedU64 481 } 482 } 483 484 var bondAssetP *uint32 485 if len(params.Args) > 3 { 486 bondAsset, err := checkIntArg(params.Args[3], "bondAsset", 33) 487 if err != nil { 488 return nil, err 489 } 490 if bondAsset >= 0 { 491 bondAssetU32 := uint32(bondAsset) 492 bondAssetP = &bondAssetU32 493 } 494 } 495 496 var penaltyComps *uint16 497 if len(params.Args) > 4 { 498 pc, err := checkIntArg(params.Args[4], "penaltyComps", 16) 499 if err != nil { 500 return nil, err 501 } 502 if pc > math.MaxUint16 { 503 return nil, fmt.Errorf("penaltyComps out of range (0, %d)", math.MaxUint16) 504 } 505 if pc > 0 { 506 pc16 := uint16(pc) 507 penaltyComps = &pc16 508 } 509 } 510 511 req := &core.BondOptionsForm{ 512 Host: params.Args[0], 513 TargetTier: targetTierP, 514 MaxBondedAmt: maxBondedP, 515 BondAssetID: bondAssetP, 516 PenaltyComps: penaltyComps, 517 } 518 return req, nil 519 } 520 521 func parsePostBondArgs(params *RawParams) (*core.PostBondForm, error) { 522 if err := checkNArgs(params, []int{1}, []int{3, 5}); err != nil { 523 return nil, err 524 } 525 bond, err := checkUIntArg(params.Args[1], "bond", 64) 526 if err != nil { 527 return nil, err 528 } 529 asset, err := checkUIntArg(params.Args[2], "asset", 32) 530 if err != nil { 531 return nil, err 532 } 533 534 var maintain *bool 535 if len(params.Args) > 3 { 536 tt, err := checkBoolArg(params.Args[3], "maintain") 537 if err != nil { 538 return nil, err 539 } 540 maintain = &tt 541 } 542 543 var cert []byte 544 if len(params.Args) > 4 { 545 cert = []byte(params.Args[4]) 546 } 547 548 asset32 := uint32(asset) 549 req := &core.PostBondForm{ 550 AppPass: params.PWArgs[0], 551 Addr: params.Args[0], 552 Cert: cert, 553 Bond: bond, 554 Asset: &asset32, 555 MaintainTier: maintain, 556 } 557 return req, nil 558 } 559 560 func parseTradeArgs(params *RawParams) (*tradeForm, error) { 561 if err := checkNArgs(params, []int{1}, []int{9}); err != nil { 562 return nil, err 563 } 564 isLimit, err := checkBoolArg(params.Args[1], "isLimit") 565 if err != nil { 566 return nil, err 567 } 568 sell, err := checkBoolArg(params.Args[2], "sell") 569 if err != nil { 570 return nil, err 571 } 572 base, err := checkUIntArg(params.Args[3], "base", 32) 573 if err != nil { 574 return nil, err 575 } 576 quote, err := checkUIntArg(params.Args[4], "quote", 32) 577 if err != nil { 578 return nil, err 579 } 580 qty, err := checkUIntArg(params.Args[5], "qty", 64) 581 if err != nil { 582 return nil, err 583 } 584 rate, err := checkUIntArg(params.Args[6], "rate", 64) 585 if err != nil { 586 return nil, err 587 } 588 tifnow, err := checkBoolArg(params.Args[7], "immediate") 589 if err != nil { 590 return nil, err 591 } 592 options, err := checkMapArg(params.Args[8], "options") 593 if err != nil { 594 return nil, err 595 } 596 req := &tradeForm{ 597 appPass: params.PWArgs[0], 598 srvForm: &core.TradeForm{ 599 Host: params.Args[0], 600 IsLimit: isLimit, 601 Sell: sell, 602 Base: uint32(base), 603 Quote: uint32(quote), 604 Qty: qty, 605 Rate: rate, 606 TifNow: tifnow, 607 Options: options, 608 }, 609 } 610 return req, nil 611 } 612 613 func parseMultiTradeArgs(params *RawParams) (*multiTradeForm, error) { 614 if err := checkNArgs(params, []int{1}, []int{6, 7}); err != nil { 615 return nil, err 616 } 617 618 sell, err := checkBoolArg(params.Args[1], "sell") 619 if err != nil { 620 return nil, err 621 } 622 623 base, err := checkUIntArg(params.Args[2], "base", 32) 624 if err != nil { 625 return nil, err 626 } 627 628 quote, err := checkUIntArg(params.Args[3], "quote", 32) 629 if err != nil { 630 return nil, err 631 } 632 633 maxLock, err := checkUIntArg(params.Args[4], "maxLock", 64) 634 if err != nil { 635 return nil, err 636 } 637 638 var p [][2]uint64 639 if err := json.Unmarshal([]byte(params.Args[5]), &p); err != nil { 640 return nil, fmt.Errorf("failed to unmarshal placements: %v", err) 641 } 642 placements := make([]*core.QtyRate, 0, len(p)) 643 for _, placement := range p { 644 placements = append(placements, &core.QtyRate{ 645 Qty: placement[0], 646 Rate: placement[1], 647 }) 648 } 649 650 options := make(map[string]string) 651 if len(params.Args) == 7 { 652 options, err = checkMapArg(params.Args[6], "options") 653 if err != nil { 654 return nil, err 655 } 656 } 657 658 return &multiTradeForm{ 659 appPass: params.PWArgs[0], 660 srvForm: &core.MultiTradeForm{ 661 Host: params.Args[0], 662 Sell: sell, 663 Base: uint32(base), 664 Quote: uint32(quote), 665 Placements: placements, 666 Options: options, 667 MaxLock: maxLock, 668 }, 669 }, nil 670 } 671 672 func parseCancelArgs(params *RawParams) (*cancelForm, error) { 673 if err := checkNArgs(params, []int{0}, []int{1}); err != nil { 674 return nil, err 675 } 676 id := params.Args[0] 677 if len(id) != orderIdLen { 678 return nil, fmt.Errorf("%w: orderID has incorrect length", errArgs) 679 } 680 oidB, err := hex.DecodeString(id) 681 if err != nil { 682 return nil, fmt.Errorf("%w: invalid order id hex", errArgs) 683 } 684 return &cancelForm{orderID: oidB}, nil 685 } 686 687 func parseSendOrWithdrawArgs(params *RawParams) (*sendOrWithdrawForm, error) { 688 if err := checkNArgs(params, []int{1}, []int{3}); err != nil { 689 return nil, err 690 } 691 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 692 if err != nil { 693 return nil, err 694 } 695 value, err := checkUIntArg(params.Args[1], "value", 64) 696 if err != nil { 697 return nil, err 698 } 699 req := &sendOrWithdrawForm{ 700 appPass: params.PWArgs[0], 701 assetID: uint32(assetID), 702 value: value, 703 address: params.Args[2], 704 } 705 return req, nil 706 } 707 708 func parseBchWithdrawArgs(params *RawParams) (appPW encode.PassBytes, recipient string, _ error) { 709 if err := checkNArgs(params, []int{1}, []int{1}); err != nil { 710 return nil, "", err 711 } 712 return params.PWArgs[0], params.Args[0], nil 713 } 714 715 func parseRescanWalletArgs(params *RawParams) (uint32, bool, error) { 716 if err := checkNArgs(params, []int{0}, []int{1, 2}); err != nil { 717 return 0, false, err 718 } 719 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 720 if err != nil { 721 return 0, false, err 722 } 723 var force bool // do not rescan with active orders by default 724 if len(params.Args) > 1 { 725 force, err = checkBoolArg(params.Args[1], "force") 726 if err != nil { 727 return 0, false, err 728 } 729 } 730 return uint32(assetID), force, nil 731 } 732 733 func parseOrderBookArgs(params *RawParams) (*orderBookForm, error) { 734 if err := checkNArgs(params, []int{0}, []int{3, 4}); err != nil { 735 return nil, err 736 } 737 base, err := checkUIntArg(params.Args[1], "base", 32) 738 if err != nil { 739 return nil, err 740 } 741 quote, err := checkUIntArg(params.Args[2], "quote", 32) 742 if err != nil { 743 return nil, err 744 } 745 var nOrders uint64 746 if len(params.Args) > 3 { 747 nOrders, err = checkUIntArg(params.Args[3], "nOrders", 64) 748 if err != nil { 749 return nil, err 750 } 751 } 752 req := &orderBookForm{ 753 host: params.Args[0], 754 base: uint32(base), 755 quote: uint32(quote), 756 nOrders: nOrders, 757 } 758 return req, nil 759 } 760 761 func parseMyOrdersArgs(params *RawParams) (*myOrdersForm, error) { 762 if err := checkNArgs(params, []int{0}, []int{0, 3}); err != nil { 763 return nil, err 764 } 765 req := new(myOrdersForm) 766 switch len(params.Args) { 767 case 3: 768 // Args 1 and 2 should be base ID and quote ID. If present, 769 // they are a pair. 770 quote, err := checkUIntArg(params.Args[2], "quote", 32) 771 if err != nil { 772 return nil, err 773 } 774 q := uint32(quote) 775 req.quote = &q 776 777 base, err := checkUIntArg(params.Args[1], "base", 32) 778 if err != nil { 779 return nil, err 780 } 781 b := uint32(base) 782 req.base = &b 783 fallthrough 784 case 1: 785 req.host = params.Args[0] 786 case 2: 787 // Received a base ID but no quote ID. 788 return nil, fmt.Errorf("%w: no market quote ID", errArgs) 789 } 790 return req, nil 791 } 792 793 func parseAppSeedArgs(params *RawParams) (encode.PassBytes, error) { 794 if err := checkNArgs(params, []int{1}, []int{0}); err != nil { 795 return nil, err 796 } 797 return params.PWArgs[0], nil 798 } 799 800 func parseDeleteArchivedRecordsArgs(params *RawParams) (form *deleteRecordsForm, err error) { 801 if err = checkNArgs(params, []int{0}, []int{0, 3}); err != nil { 802 return nil, err 803 } 804 form = new(deleteRecordsForm) 805 switch len(params.Args) { 806 case 3: 807 form.ordersFileStr = params.Args[2] 808 fallthrough 809 case 2: 810 form.matchesFileStr = params.Args[1] 811 fallthrough 812 case 1: 813 olderThanStr := params.Args[0] 814 if olderThanStr == "" || olderThanStr == "0" { 815 break 816 } 817 olderThanMs, err := strconv.ParseInt(olderThanStr, 10, 64) 818 if err != nil { 819 return nil, fmt.Errorf("invalid older than time %q: %v", olderThanStr, err) 820 } 821 t := time.UnixMilli(olderThanMs) 822 form.olderThan = &t 823 } 824 return form, nil 825 } 826 827 func parseWalletPeersArgs(params *RawParams) (uint32, error) { 828 if err := checkNArgs(params, []int{0}, []int{1}); err != nil { 829 return 0, err 830 } 831 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 832 if err != nil { 833 return 0, err 834 } 835 return uint32(assetID), nil 836 } 837 838 func parseAddRemoveWalletPeerArgs(params *RawParams) (form *addRemovePeerForm, err error) { 839 if err = checkNArgs(params, []int{0}, []int{2}); err != nil { 840 return nil, err 841 } 842 form = new(addRemovePeerForm) 843 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 844 if err != nil { 845 return nil, fmt.Errorf("invalid assetID: %v", err) 846 } 847 form.assetID = uint32(assetID) 848 form.address = params.Args[1] 849 return form, nil 850 } 851 852 func parseNotificationsArgs(params *RawParams) (int, error) { 853 if err := checkNArgs(params, []int{0}, []int{1}); err != nil { 854 return 0, err 855 } 856 num, err := checkUIntArg(params.Args[0], "num", 32) 857 if err != nil { 858 return 0, fmt.Errorf("invalid num: %v", err) 859 } 860 return int(num), nil 861 } 862 863 func parseMktWithHost(host, baseID, quoteID string) (*mm.MarketWithHost, error) { 864 mkt := new(mm.MarketWithHost) 865 mkt.Host = host 866 base, err := checkUIntArg(baseID, "baseID", 32) 867 if err != nil { 868 return nil, fmt.Errorf("invalid baseID: %v", err) 869 } 870 mkt.BaseID = uint32(base) 871 quote, err := checkUIntArg(quoteID, "quoteID", 32) 872 if err != nil { 873 return nil, fmt.Errorf("invalid quoteID: %v", err) 874 } 875 mkt.QuoteID = uint32(quote) 876 return mkt, nil 877 } 878 879 func parseMMAvailableBalancesArgs(params *RawParams) (*mmAvailableBalancesForm, error) { 880 if err := checkNArgs(params, []int{0}, []int{4}); err != nil { 881 return nil, err 882 } 883 form := new(mmAvailableBalancesForm) 884 form.cfgFilePath = params.Args[0] 885 mkt, err := parseMktWithHost(params.Args[1], params.Args[2], params.Args[3]) 886 if err != nil { 887 return nil, err 888 } 889 form.mkt = mkt 890 return form, nil 891 } 892 893 func parseBotDiffs(balanceArg string) (map[uint32]int64, error) { 894 balances := make([][2]int64, 0) 895 err := json.Unmarshal([]byte(balanceArg), &balances) 896 if err != nil { 897 return nil, fmt.Errorf("failed to unmarshal bot balances: %v", err) 898 } 899 900 toReturn := make(map[uint32]int64) 901 for _, b := range balances { 902 toReturn[uint32(b[0])] = b[1] 903 } 904 905 return toReturn, nil 906 } 907 908 func parseBotBalances(balanceArg string) (map[uint32]uint64, error) { 909 diffs, err := parseBotDiffs(balanceArg) 910 if err != nil { 911 return nil, err 912 } 913 914 toReturn := make(map[uint32]uint64) 915 for k, v := range diffs { 916 if v < 0 { 917 return nil, fmt.Errorf("balances must be positive") 918 } 919 toReturn[k] = uint64(v) 920 } 921 922 return toReturn, nil 923 } 924 925 func parseStartBotArgs(params *RawParams) (*startBotForm, error) { 926 if err := checkNArgs(params, []int{1}, []int{6}); err != nil { 927 return nil, err 928 } 929 form := new(startBotForm) 930 form.appPass = params.PWArgs[0] 931 form.cfgFilePath = params.Args[0] 932 933 mkt, err := parseMktWithHost(params.Args[1], params.Args[2], params.Args[3]) 934 if err != nil { 935 return nil, err 936 } 937 form.mkt = mkt 938 939 return form, nil 940 } 941 942 func parseStopBotArgs(params *RawParams) (*mm.MarketWithHost, error) { 943 if err := checkNArgs(params, []int{0}, []int{3}); err != nil { 944 return nil, err 945 } 946 return parseMktWithHost(params.Args[0], params.Args[1], params.Args[2]) 947 } 948 949 func parseUpdateRunningBotArgs(params *RawParams) (*updateRunningBotForm, error) { 950 if err := checkNArgs(params, []int{0}, []int{4, 6}); err != nil { 951 return nil, err 952 } 953 954 form := new(updateRunningBotForm) 955 956 form.cfgFilePath = params.Args[0] 957 958 mkt, err := parseMktWithHost(params.Args[1], params.Args[2], params.Args[3]) 959 if err != nil { 960 return nil, err 961 } 962 form.mkt = mkt 963 964 dexBals := make(map[uint32]int64) 965 cexBals := make(map[uint32]int64) 966 967 if len(params.Args) > 4 { 968 dexBals, err = parseBotDiffs(params.Args[4]) 969 if err != nil { 970 return nil, fmt.Errorf("failed to unmarshal dex balance diffs: %v", err) 971 } 972 } 973 974 if len(params.Args) > 5 { 975 cexBals, err = parseBotDiffs(params.Args[5]) 976 if err != nil { 977 return nil, fmt.Errorf("failed to unmarshal cex balance diffs: %v", err) 978 } 979 } 980 981 form.balances = &mm.BotInventoryDiffs{ 982 DEX: dexBals, 983 CEX: cexBals, 984 } 985 986 return form, nil 987 } 988 989 func parseUpdateRunningBotInventoryArgs(params *RawParams) (*updateRunningBotInventoryForm, error) { 990 if err := checkNArgs(params, []int{0}, []int{5}); err != nil { 991 return nil, err 992 } 993 994 form := new(updateRunningBotInventoryForm) 995 996 mkt, err := parseMktWithHost(params.Args[0], params.Args[1], params.Args[2]) 997 if err != nil { 998 return nil, err 999 } 1000 form.mkt = mkt 1001 1002 dexBals, err := parseBotDiffs(params.Args[3]) 1003 if err != nil { 1004 return nil, fmt.Errorf("failed to unmarshal dex balance diffs: %v", err) 1005 } 1006 1007 cexBals, err := parseBotDiffs(params.Args[4]) 1008 if err != nil { 1009 return nil, fmt.Errorf("failed to unmarshal cex balance diffs: %v", err) 1010 } 1011 1012 form.balances = &mm.BotInventoryDiffs{ 1013 DEX: dexBals, 1014 CEX: cexBals, 1015 } 1016 1017 return form, nil 1018 } 1019 1020 func parseSetVSPArgs(params *RawParams) (*setVSPForm, error) { 1021 if err := checkNArgs(params, []int{0}, []int{2}); err != nil { 1022 return nil, err 1023 } 1024 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 1025 if err != nil { 1026 return nil, fmt.Errorf("invalid assetID: %v", err) 1027 } 1028 return &setVSPForm{ 1029 assetID: uint32(assetID), 1030 addr: params.Args[1], 1031 }, nil 1032 } 1033 1034 func parsePurchaseTicketsArgs(params *RawParams) (*purchaseTicketsForm, error) { 1035 if err := checkNArgs(params, []int{1}, []int{2}); err != nil { 1036 return nil, err 1037 } 1038 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 1039 if err != nil { 1040 return nil, fmt.Errorf("invalid assetID: %v", err) 1041 } 1042 num, err := checkUIntArg(params.Args[1], "num", 32) 1043 if err != nil { 1044 return nil, fmt.Errorf("invalid num: %v", err) 1045 } 1046 return &purchaseTicketsForm{ 1047 assetID: uint32(assetID), 1048 num: int(num), 1049 appPass: params.PWArgs[0], 1050 }, nil 1051 } 1052 1053 func parseStakeStatusArgs(params *RawParams) (uint32, error) { 1054 if err := checkNArgs(params, []int{0}, []int{1}); err != nil { 1055 return 0, err 1056 } 1057 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 1058 if err != nil { 1059 return 0, fmt.Errorf("invalid assetID: %v", err) 1060 } 1061 return uint32(assetID), nil 1062 } 1063 1064 func parseSetVotingPreferencesArgs(params *RawParams) (*setVotingPreferencesForm, error) { 1065 err := checkNArgs(params, []int{0}, []int{1, 4}) 1066 if err != nil { 1067 return nil, err 1068 } 1069 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 1070 if err != nil { 1071 return nil, fmt.Errorf("invalid assetID: %v", err) 1072 } 1073 var p string 1074 form := &setVotingPreferencesForm{ 1075 assetID: uint32(assetID), 1076 } 1077 switch len(params.Args) { 1078 case 4: 1079 p = params.Args[3] 1080 if p != "" { 1081 form.treasuryPolicy, err = checkMapArg(p, "treasury policy") 1082 if err != nil { 1083 return nil, err 1084 } 1085 } 1086 fallthrough 1087 case 3: 1088 p = params.Args[2] 1089 if p != "" { 1090 form.tSpendPolicy, err = checkMapArg(p, "tspend policy") 1091 if err != nil { 1092 return nil, err 1093 } 1094 } 1095 fallthrough 1096 case 2: 1097 p = params.Args[1] 1098 if p != "" { 1099 form.voteChoices, err = checkMapArg(p, "vote choices") 1100 if err != nil { 1101 return nil, err 1102 } 1103 } 1104 } 1105 return form, nil 1106 } 1107 1108 func parseTxHistoryArgs(params *RawParams) (*txHistoryForm, error) { 1109 err := checkNArgs(params, []int{0}, []int{1, 4}) 1110 if err != nil { 1111 return nil, err 1112 } 1113 1114 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 1115 if err != nil { 1116 return nil, fmt.Errorf("invalid assetID: %v", err) 1117 } 1118 1119 var num int64 1120 if len(params.Args) > 1 { 1121 num, err = checkIntArg(params.Args[1], "num", 64) 1122 if err != nil { 1123 return nil, fmt.Errorf("invalid num: %v", err) 1124 } 1125 } 1126 1127 var refID *string 1128 var past bool 1129 if len(params.Args) > 2 { 1130 if len(params.Args) != 4 { 1131 return nil, fmt.Errorf("refID provided without past") 1132 } 1133 1134 refID = ¶ms.Args[2] 1135 1136 past, err = checkBoolArg(params.Args[3], "past") 1137 if err != nil { 1138 return nil, err 1139 } 1140 } 1141 1142 return &txHistoryForm{ 1143 assetID: uint32(assetID), 1144 num: int(num), 1145 refID: refID, 1146 past: past, 1147 }, nil 1148 } 1149 1150 type walletTxForm struct { 1151 assetID uint32 1152 txID string 1153 } 1154 1155 func parseWalletTxArgs(params *RawParams) (*walletTxForm, error) { 1156 err := checkNArgs(params, []int{0}, []int{2}) 1157 if err != nil { 1158 return nil, err 1159 } 1160 1161 assetID, err := checkUIntArg(params.Args[0], "assetID", 32) 1162 if err != nil { 1163 return nil, fmt.Errorf("invalid assetID: %v", err) 1164 } 1165 1166 return &walletTxForm{ 1167 assetID: uint32(assetID), 1168 txID: params.Args[1], 1169 }, nil 1170 }