-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
format.go
184 lines (171 loc) · 6.83 KB
/
format.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package stellar
import (
"fmt"
"github.com/keybase/client/go/libkb"
"github.com/keybase/client/go/protocol/stellar1"
"github.com/keybase/stellarnet"
)
func FormatCurrency(mctx libkb.MetaContext, amount string, code stellar1.OutsideCurrencyCode, rounding stellarnet.FmtRoundingBehavior) (string, error) {
conf, err := mctx.G().GetStellar().GetServerDefinitions(mctx.Ctx())
if err != nil {
return "", err
}
currency, ok := conf.Currencies[code]
if !ok {
return "", fmt.Errorf("FormatCurrency error: cannot find curency code %q", code)
}
return stellarnet.FmtCurrency(amount, rounding, currency.Symbol.Symbol, currency.Symbol.Postfix)
}
// FormatCurrencyWithCodeSuffix will return a fiat currency amount formatted with
// its currency code suffix at the end, like "$123.12 CLP"
func FormatCurrencyWithCodeSuffix(mctx libkb.MetaContext, amount string, code stellar1.OutsideCurrencyCode, rounding stellarnet.FmtRoundingBehavior) (string, error) {
conf, err := mctx.G().GetStellar().GetServerDefinitions(mctx.Ctx())
if err != nil {
return "", err
}
currency, ok := conf.Currencies[code]
if !ok {
return "", fmt.Errorf("FormatCurrencyWithCodeSuffix error: cannot find curency code %q", code)
}
return stellarnet.FmtCurrencyWithCodeSuffix(amount, rounding, string(code), currency.Symbol.Symbol, currency.Symbol.Postfix)
}
// Return an error if asset is completely outside of what we understand, like
// asset unknown types or unexpected length.
func assertAssetIsSane(asset stellar1.Asset) error {
switch asset.Type {
case "credit_alphanum4", "credit_alphanum12":
case "alphanum4", "alphanum12": // These prefixes that are missing "credit_" shouldn't show up, but just to be on the safe side.
default:
return fmt.Errorf("unrecognized asset type: %v", asset.Type)
}
// Sanity check asset code very loosely. We know tighter bounds but there's no need to fail here.
if len(asset.Code) == 0 || len(asset.Code) >= 20 {
return fmt.Errorf("invalid asset code: %v", asset.Code)
}
return nil
}
// Example: "157.5000000 XLM"
// Example: "12.9000000 USD"
//
// (where USD is a non-native asset issued by someone).
//
// User interfaces should be careful to never give user just amount + asset
// code, but annotate when it's a non-native asset and make Issuer ID and
// Verified Domain visible.
// If you are coming from CLI, FormatAmountDescriptionAssetEx might be a better
// choice which is more verbose about non-native assets.
func FormatAmountDescriptionAsset(mctx libkb.MetaContext, amount string, asset stellar1.Asset) (string, error) {
if asset.IsNativeXLM() {
return FormatAmountDescriptionXLM(mctx, amount)
}
if err := assertAssetIsSane(asset); err != nil {
return "", err
}
// Sanity check asset issuer.
if _, err := libkb.ParseStellarAccountID(asset.Issuer); err != nil {
return "", fmt.Errorf("asset issuer is not account ID: %v", asset.Issuer)
}
return FormatAmountWithSuffix(mctx, amount, false /* precisionTwo */, false /* simplify */, asset.Code)
}
// FormatAmountDescriptionAssetEx is a more verbose version of FormatAmountDescriptionAsset.
// In case of non-native asset, it includes issuer domain (or "Unknown") and issuer ID.
// Example: "157.5000000 XLM"
// Example: "1,000.15 CATS/catmoney.example.com (GDWVJEG7CMYKRYGB2MWSRZNSPCWIGGA4FRNFTQBIR6RAEPNEGGEH4XYZ)"
// Example: "1,000.15 BTC/Unknown (GBPEHURSE52GCBRPDWNV2VL3HRLCI42367OGRPBOO3AW6VAYEW5EO5PM)"
func FormatAmountDescriptionAssetEx(mctx libkb.MetaContext, amount string, asset stellar1.Asset) (string, error) {
if asset.IsNativeXLM() {
return FormatAmountDescriptionXLM(mctx, amount)
}
if err := assertAssetIsSane(asset); err != nil {
return "", err
}
// Sanity check asset issuer.
issuerAccountID, err := libkb.ParseStellarAccountID(asset.Issuer)
if err != nil {
return "", fmt.Errorf("asset issuer is not account ID: %v", asset.Issuer)
}
amountFormatted, err := FormatAmount(mctx, amount, false /* precisionTwo */, stellarnet.Round)
if err != nil {
return "", err
}
var issuerDesc string
if asset.VerifiedDomain != "" {
issuerDesc = asset.VerifiedDomain
} else {
issuerDesc = "Unknown"
}
return fmt.Sprintf("%s %s/%s (%s)", amountFormatted, asset.Code, issuerDesc, issuerAccountID.String()), nil
}
// FormatAmountDescriptionAssetEx2 is like FormatAmountDescriptionAssetEx,
// except that it only shows one of issuer domain and issuer account ID. When
// issuer domain is available, the domain is shown. Otherwise account ID is
// used.
// Example: "157.5000000 XLM"
// Example: "1,000.15 CATS/catmoney.example.com
// Example: "1,000.15 BTC/GBPEHURSE52GCBRPDWNV2VL3HRLCI42367OGRPBOO3AW6VAYEW5EO5PM"
func FormatAmountDescriptionAssetEx2(mctx libkb.MetaContext, amount string, asset stellar1.Asset) (string, error) {
if asset.IsNativeXLM() {
return FormatAmountDescriptionXLM(mctx, amount)
}
if err := assertAssetIsSane(asset); err != nil {
return "", err
}
// Sanity check asset issuer.
issuerAccountID, err := libkb.ParseStellarAccountID(asset.Issuer)
if err != nil {
return "", fmt.Errorf("asset issuer is not account ID: %v", asset.Issuer)
}
amountFormatted, err := FormatAmount(mctx, amount, false /* precisionTwo */, stellarnet.Round)
if err != nil {
return "", err
}
var issuerDesc string
if asset.VerifiedDomain != "" {
issuerDesc = asset.VerifiedDomain
} else {
issuerDesc = issuerAccountID.String()
}
return fmt.Sprintf("%s %s/%s", amountFormatted, asset.Code, issuerDesc), nil
}
// FormatAssetIssuerString returns "Unknown issuer" if asset does not have a
// verified domain, or returns asset verified domain if it does (e.g.
// "example.com").
func FormatAssetIssuerString(asset stellar1.Asset) string {
if asset.VerifiedDomain != "" {
return asset.VerifiedDomain
}
iaid := asset.IssuerString()
iaidLen := len(iaid)
switch {
case iaidLen > 16:
return iaid[:8] + "..." + iaid[iaidLen-8:]
case iaidLen > 0:
return iaid
default:
return "Unknown issuer"
}
}
// Example: "157.5000000 XLM"
func FormatAmountDescriptionXLM(mctx libkb.MetaContext, amount string) (string, error) {
// Do not simplify XLM amounts, all zeroes are important because
// that's the exact number of digits that Stellar protocol
// supports.
return FormatAmountWithSuffix(mctx, amount, false /* precisionTwo */, false /* simplify */, "XLM")
}
func FormatAmountWithSuffix(mctx libkb.MetaContext, amount string, precisionTwo bool, simplify bool, suffix string) (string, error) {
formatted, err := FormatAmount(mctx, amount, precisionTwo, stellarnet.Round)
if err != nil {
return "", err
}
if simplify {
formatted = libkb.StellarSimplifyAmount(formatted)
}
return fmt.Sprintf("%s %s", formatted, suffix), nil
}
func FormatAmount(mctx libkb.MetaContext, amount string, precisionTwo bool, rounding stellarnet.FmtRoundingBehavior) (string, error) {
if amount == "" {
EmptyAmountStack(mctx)
return "", fmt.Errorf("empty amount")
}
return stellarnet.FmtAmount(amount, precisionTwo, rounding)
}