|
| 1 | +// Copyright 2022 Shift Crypto AG |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +package exchanges |
| 16 | + |
| 17 | +import ( |
| 18 | + "net/http" |
| 19 | + |
| 20 | + "github.com/digitalbitbox/bitbox-wallet-app/backend/accounts" |
| 21 | + "github.com/digitalbitbox/bitbox-wallet-app/util/logging" |
| 22 | +) |
| 23 | + |
| 24 | +// ErrorCode are errors that are represented by an error code. This helps the frontend to translate |
| 25 | +// error messages. |
| 26 | +type ErrorCode string |
| 27 | + |
| 28 | +func (e ErrorCode) Error() string { |
| 29 | + return string(e) |
| 30 | +} |
| 31 | + |
| 32 | +const ( |
| 33 | + // ErrAddressNotFound is returned if an address provided for verification is not in the list of unused addresses. |
| 34 | + ErrAddressNotFound ErrorCode = "addressNotFound" |
| 35 | + |
| 36 | + // ErrUserAbort is returned if the user aborted the current operation. |
| 37 | + ErrUserAbort ErrorCode = "userAbort" |
| 38 | +) |
| 39 | + |
| 40 | +// regionCodes is an array containing ISO 3166-1 alpha-2 code of all regions. |
| 41 | +// Source: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 |
| 42 | +var regionCodes = []string{ |
| 43 | + "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", |
| 44 | + "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", |
| 45 | + "BT", "BV", "BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", |
| 46 | + "CO", "CR", "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", |
| 47 | + "EG", "EH", "ER", "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", "GA", "GB", "GD", "GE", "GF", |
| 48 | + "GG", "GH", "GI", "GL", "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", "GW", "GY", "HK", "HM", |
| 49 | + "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", "IT", "JE", "JM", |
| 50 | + "JO", "JP", "KE", "KG", "KH", "KI", "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", |
| 51 | + "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", |
| 52 | + "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", |
| 53 | + "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", "NR", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", |
| 54 | + "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", |
| 55 | + "SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "SS", |
| 56 | + "ST", "SV", "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", "TK", "TL", "TM", "TN", "TO", |
| 57 | + "TR", "TT", "TV", "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI", |
| 58 | + "VN", "VU", "WF", "WS", "YE", "YT", "ZA", "ZM", "ZW"} |
| 59 | + |
| 60 | +// ExchangeRegionList contains a list of ExchangeRegion objects. |
| 61 | +type ExchangeRegionList struct { |
| 62 | + Regions []ExchangeRegion `json:"regions"` |
| 63 | +} |
| 64 | + |
| 65 | +// ExchangeRegion contains the ISO 3166-1 alpha-2 code of a specific region and a boolean |
| 66 | +//for each exchange, indicating if that exchange is enabled for the region. |
| 67 | +type ExchangeRegion struct { |
| 68 | + Code string `json:"code"` |
| 69 | + IsMoonpayEnabled bool `json:"isMoonpayEnabled"` |
| 70 | + IsPocketEnabled bool `json:"isPocketEnabled"` |
| 71 | +} |
| 72 | + |
| 73 | +// PaymentMethod type is used for payment options in exchange deals. |
| 74 | +type PaymentMethod string |
| 75 | + |
| 76 | +const ( |
| 77 | + // CardPayment is a payment with credit/debit card. |
| 78 | + CardPayment PaymentMethod = "card" |
| 79 | + // BankTransferPayment is a payment with bank transfer. |
| 80 | + BankTransferPayment PaymentMethod = "bank-transfer" |
| 81 | +) |
| 82 | + |
| 83 | +// ExchangeDeal represents a specific purchase option of an exchange. |
| 84 | +// - Fee indicates form the percentage that goes to the exchange in a float representation (e.g. 0.01 -> 1%). |
| 85 | +// - Payment is the payment method offered in the deal (usually different payment methods bring different fees). |
| 86 | +// - IsFast is usually associated with card payments. It is used by the frontend to display the `fast` tag in deals list. |
| 87 | +type ExchangeDeal struct { |
| 88 | + Fee float32 `json:"fee"` |
| 89 | + Payment PaymentMethod `json:"payment"` |
| 90 | + IsFast bool `json:"isFast"` |
| 91 | +} |
| 92 | + |
| 93 | +// ExchangeDeals list the name of a specific exchange and the list of available deals offered by that exchange. |
| 94 | +type ExchangeDeals struct { |
| 95 | + ExchangeName string `json:"exchangeName"` |
| 96 | + Deals []ExchangeDeal `json:"deals"` |
| 97 | +} |
| 98 | + |
| 99 | +// ListExchangesByRegion populates an array of `ExchangeRegion` objects representing the availability |
| 100 | +// of the various exchanges in each of them, for the provided account. |
| 101 | +// For each region, an exchange is enabled if it supports the account coin and it is active in that region. |
| 102 | +// NOTE: if one of the endpoint fails for any reason, the related exchange will be set as available in any |
| 103 | +// region by default (for the supported coins). |
| 104 | +func ListExchangesByRegion(account accounts.Interface, httpClient *http.Client) ExchangeRegionList { |
| 105 | + moonpayRegions, moonpayError := GetMoonpaySupportedRegions(httpClient) |
| 106 | + log := logging.Get().WithGroup("exchanges") |
| 107 | + if moonpayError != nil { |
| 108 | + log.Error(moonpayError) |
| 109 | + } |
| 110 | + |
| 111 | + pocketRegions, pocketError := GetPocketSupportedRegions(httpClient) |
| 112 | + if pocketError != nil { |
| 113 | + log.Error(pocketError) |
| 114 | + } |
| 115 | + |
| 116 | + isMoonpaySupported := IsMoonpaySupported(account.Coin().Code()) |
| 117 | + isPocketSupported := IsPocketSupported(account) |
| 118 | + |
| 119 | + exchangeRegions := ExchangeRegionList{} |
| 120 | + for _, code := range regionCodes { |
| 121 | + // default behavior is to show the exchange if the supported regions check fails. |
| 122 | + moonpayEnabled, pocketEnabled := true, true |
| 123 | + if moonpayError == nil { |
| 124 | + _, moonpayEnabled = moonpayRegions[code] |
| 125 | + } |
| 126 | + if pocketError == nil { |
| 127 | + _, pocketEnabled = pocketRegions[code] |
| 128 | + } |
| 129 | + exchangeRegions.Regions = append(exchangeRegions.Regions, ExchangeRegion{ |
| 130 | + Code: code, |
| 131 | + IsMoonpayEnabled: moonpayEnabled && isMoonpaySupported, |
| 132 | + IsPocketEnabled: pocketEnabled && isPocketSupported, |
| 133 | + }) |
| 134 | + } |
| 135 | + |
| 136 | + return exchangeRegions |
| 137 | +} |
0 commit comments