2
2
{-# LANGUAGE CPP #-}
3
3
4
4
module Functora.Bolt11
5
- ( Bolt11 (.. ),
6
- decodeBolt11 ,
7
- Hex (.. ),
8
- Multiplier (.. ),
9
- Network (.. ),
5
+ ( Hex (.. ),
6
+ inspectHex ,
10
7
Tag (.. ),
11
- Bolt11Hrp (.. ),
12
- Bolt11HrpAmt (.. ),
13
8
isPaymentHash ,
9
+ Network (.. ),
10
+ Multiplier (.. ),
11
+ Bolt11HrpAmt (.. ),
12
+ inspectBolt11HrpAmt ,
13
+ Bolt11Hrp (.. ),
14
+ Bolt11 (.. ),
15
+ decodeBolt11 ,
14
16
)
15
17
where
16
18
@@ -31,11 +33,15 @@ import Prelude (Show (..), error, splitAt, take)
31
33
newtype Hex = Hex
32
34
{ unHex :: ByteString
33
35
}
34
- deriving newtype (Eq , Ord )
35
- deriving stock (Data , Generic )
36
+ deriving stock (Eq , Ord , Show , Data , Generic )
36
37
37
- instance Show Hex where
38
- show (Hex bs) = BL. unpack (BS. toLazyByteString (BS. byteStringHex bs))
38
+ inspectHex :: forall a . (From String a ) => Hex -> a
39
+ inspectHex =
40
+ from @ String @ a
41
+ . BL. unpack
42
+ . BS. toLazyByteString
43
+ . BS. byteStringHex
44
+ . unHex
39
45
40
46
instance IsString Hex where
41
47
fromString =
@@ -73,37 +79,69 @@ isPaymentHash :: Tag -> Bool
73
79
isPaymentHash PaymentHash {} = True
74
80
isPaymentHash _ = False
75
81
76
- data Multiplier = Milli | Micro | Nano | Pico
77
- deriving stock (Eq , Ord , Show , Data , Generic )
78
-
79
82
data Network
80
83
= BitcoinMainnet
81
84
| BitcoinTestnet
82
85
| BitcoinRegtest
83
86
| BitcoinSignet
84
87
deriving stock (Eq , Ord , Show , Data , Generic )
85
88
89
+ data Multiplier = Milli | Micro | Nano | Pico
90
+ deriving stock (Eq , Ord , Show , Data , Generic )
91
+
86
92
data Bolt11HrpAmt = Bolt11HrpAmt
87
93
{ bolt11HrpAmtNum :: Integer ,
88
94
bolt11HrpAmtMul :: Multiplier
89
95
}
90
- deriving stock (Eq , Ord , Data , Generic )
91
-
92
- instance Show Bolt11HrpAmt where
93
- show (Bolt11HrpAmt amt mul) =
94
- if sat > 1_000_000
95
- then inspectBtcNum btc <> " BTC"
96
- else
97
- if (round sat) % 1 == sat
98
- then inspectBtcNum sat <> " Satoshi"
99
- else inspectBtcNum msat <> " Millisatoshi"
100
- where
101
- btc :: Rational
102
- btc = (amt % 1 ) * multiplierRatio mul
103
- sat :: Rational
104
- sat = btc * 1_0000_0000
105
- msat :: Rational
106
- msat = sat * 1000
96
+ deriving stock (Eq , Ord , Show , Data , Generic )
97
+
98
+ inspectBolt11HrpAmt ::
99
+ ( From String a ,
100
+ Semigroup a ,
101
+ IsString a
102
+ ) =>
103
+ Bolt11HrpAmt ->
104
+ a
105
+ inspectBolt11HrpAmt (Bolt11HrpAmt amt mul) =
106
+ if sat > 1_000_000
107
+ then inspectBolt11HrpAmt' btc <> " BTC"
108
+ else
109
+ if (round sat) % 1 == sat
110
+ then inspectBolt11HrpAmt' sat <> " Satoshi"
111
+ else inspectBolt11HrpAmt' msat <> " Millisatoshi"
112
+ where
113
+ btc :: Rational
114
+ btc = (amt % 1 ) * multiplierRatio mul
115
+ sat :: Rational
116
+ sat = btc * 1_0000_0000
117
+ msat :: Rational
118
+ msat = sat * 1000
119
+
120
+ inspectBolt11HrpAmt' ::
121
+ forall a b .
122
+ ( From String a ,
123
+ From b Integer ,
124
+ Integral b
125
+ ) =>
126
+ Ratio b ->
127
+ a
128
+ inspectBolt11HrpAmt' =
129
+ inspectRatio
130
+ RatioFormat
131
+ { ratioFormatDoRounding = True ,
132
+ ratioFormatThousandsSeparator = mempty ,
133
+ ratioFormatDecimalPlacesAfterNonZero = Just 12 , -- Pico
134
+ ratioFormatDecimalPlacesTotalLimit = Just 12 , -- Pico
135
+ ratioFormatDecimalPlacesTotalLimitOverflow = DecimalPlacesOverflowExponent
136
+ }
137
+
138
+ multiplierRatio :: Multiplier -> Rational
139
+ multiplierRatio m =
140
+ case m of
141
+ Milli -> 1 % 1000
142
+ Micro -> 1 % 1000000
143
+ Nano -> 1 % 1000000000
144
+ Pico -> 1 % 1000000000000
107
145
108
146
data Bolt11Hrp = Bolt11Hrp
109
147
{ bolt11HrpNet :: Network ,
@@ -142,8 +180,8 @@ parseHrpAmount = do
142
180
mul <- parseMultiplier
143
181
pure $ Bolt11HrpAmt amt mul
144
182
145
- hrpParser :: Parser Bolt11Hrp
146
- hrpParser = do
183
+ parseHrp :: Parser Bolt11Hrp
184
+ parseHrp = do
147
185
_ <- char ' l'
148
186
_ <- char ' n'
149
187
net <- parseNetwork
@@ -205,39 +243,13 @@ tagsParser ws
205
243
mtag
206
244
207
245
decodeBolt11 :: Text -> Either String Bolt11
208
- decodeBolt11 txt = do
209
- (hrp, w5s ) <- first show $ Bech32. decodeLenient txt
210
- let (timestampBits, rest) = splitAt 7 $ Bech32. dataPartToWords w5s
246
+ decodeBolt11 raw = do
247
+ (rawHrp, rawDp ) <- first show $ Bech32. decodeLenient raw
248
+ let (timestampBits, rest) = splitAt 7 $ Bech32. dataPartToWords rawDp
211
249
timestamp = w5int timestampBits
212
250
(tags, leftover) = tagsParser rest
213
251
sig <- case leftover of
214
252
Sig ws -> maybe (Left " corrupt" ) Right (Bech32. toBase256 ws)
215
253
Unk left -> Left (" corrupt, leftover: " ++ show (Hex (w5bs left)))
216
- parsedHrp <- parseOnly hrpParser $ Bech32. humanReadablePartToText hrp
217
- Right (Bolt11 parsedHrp timestamp tags (Hex (BS. pack sig)))
218
-
219
- multiplierRatio :: Multiplier -> Rational
220
- multiplierRatio m =
221
- case m of
222
- Milli -> 1 % 1000
223
- Micro -> 1 % 1000000
224
- Nano -> 1 % 1000000000
225
- Pico -> 1 % 1000000000000
226
-
227
- inspectBtcNum ::
228
- forall a b .
229
- ( From String a ,
230
- From b Integer ,
231
- Integral b
232
- ) =>
233
- Ratio b ->
234
- a
235
- inspectBtcNum =
236
- inspectRatio
237
- RatioFormat
238
- { ratioFormatDoRounding = True ,
239
- ratioFormatThousandsSeparator = mempty ,
240
- ratioFormatDecimalPlacesAfterNonZero = Just 12 , -- Pico
241
- ratioFormatDecimalPlacesTotalLimit = Just 12 , -- Pico
242
- ratioFormatDecimalPlacesTotalLimitOverflow = DecimalPlacesOverflowExponent
243
- }
254
+ hrp <- parseOnly parseHrp $ Bech32. humanReadablePartToText rawHrp
255
+ Right (Bolt11 hrp timestamp tags (Hex (BS. pack sig)))
0 commit comments