|
37 | 37 | from ..util import commas, is_listlike, is_mapping |
38 | 38 | from ..defaults import INVOICE_CURRENCY, INVOICE_ROWS, INVOICE_STRFTIME, INVOICE_DUE, MM_IN, FILENAME_FORMAT |
39 | 39 | from ..layout import Region, Text, Image, Box, Coordinate, layout_pdf |
40 | | -from .ethereum import tokeninfo, tokenprices, TokenInfo # , tokenratio |
| 40 | +from .ethereum import tokeninfo, tokenprices, tokenknown |
41 | 41 |
|
42 | 42 | """ |
43 | 43 | Invoice artifacts: |
@@ -215,27 +215,7 @@ def cryptocurrency_proxy( name, decimals=None, chain=None, w3_url=None, use_prov |
215 | 215 | except Exception as exc: |
216 | 216 | log.info( f"Could not identify currency {name!r} as an ERC-20 Token: {exc}" ) |
217 | 217 | # Not a known Token; a core known Cryptocurrency? |
218 | | - return cryptocurrency_known( name, decimals=decimals ) |
219 | | - |
220 | | - |
221 | | -def cryptocurrency_known( name, decimals=None ): |
222 | | - """If it is a recognized core supported Cryptocurrency, return a TokenInfo useful for formatting. |
223 | | - Since Bitcoin (and other similar cryptocurrencies) are typically assumed to have 8 decimal |
224 | | - places (the Sat(oshi) is 1/10^8 of a Bitcoin), we'll make the default decimals//3 precision work |
225 | | - out to 8). |
226 | | -
|
227 | | - """ |
228 | | - try: |
229 | | - symbol = Account.supported( name ) |
230 | | - except ValueError as exc: |
231 | | - log.info( f"Failed to identify currency {name!r} as a supported Cryptocurrency: {exc}" ) |
232 | | - raise |
233 | | - return TokenInfo( |
234 | | - name = Account.CRYPTO_SYMBOLS[symbol], |
235 | | - symbol = symbol, |
236 | | - decimals = 24 if decimals is None else decimals, |
237 | | - icon = next( ( Path( __file__ ).resolve().parent / "Cryptos" ).glob( symbol + '*.*' ), None ), |
238 | | - ) |
| 218 | + return tokenknown( name, decimals=decimals ) |
239 | 219 |
|
240 | 220 |
|
241 | 221 | class Invoice: |
@@ -323,19 +303,31 @@ def __init__( |
323 | 303 | # and an Ethereum account provided, the buyer can pay in BTC to the Bitcoin account, or in |
324 | 304 | # ETH or WBTC to the Ethereum account. This will add the symbols for all Crypto proxies to |
325 | 305 | # currencies_account. |
326 | | - currencies_proxy = {} |
| 306 | + currencies_alias = {} # eg. { "WBTC": TokenInfo( "BTC", ... ), ... } |
| 307 | + currencies_proxy = {} # eg. { "BTC": TokenInfo( "WBTC", ... ), ... } |
327 | 308 | for c in currencies: |
328 | 309 | try: |
329 | | - currencies_proxy[c] = tokeninfo( c, w3_url=w3_url, use_provider=use_provider ) |
| 310 | + currencies_proxy[c] = alias = tokeninfo( c, w3_url=w3_url, use_provider=use_provider ) |
330 | 311 | except Exception as exc: |
331 | | - log.info( f"Failed to find proxy for Invoice currency {c}: {exc}" ) |
| 312 | + # May fail later, if no conversions provided for this currency |
| 313 | + log.warning( f"Failed to find proxy for Invoice currency {c}: {exc}" ) |
332 | 314 | else: |
333 | | - # Yup; a proxy for a Crypto Eg. BTC -> WBTC was found, or a native ERC-20 eg. USDC |
| 315 | + # Yup; a proxy for a Crypto eg. BTC -> WBTC was found, or a native ERC-20 eg. USDC |
334 | 316 | # was found; associate it with any ETH account provided. |
335 | 317 | if eth := currencies_account.get( 'ETH' ): |
336 | 318 | currencies_account[currencies_proxy[c].symbol] = eth |
| 319 | + try: |
| 320 | + known = tokenknown( c ) |
| 321 | + except Exception: |
| 322 | + pass |
| 323 | + else: |
| 324 | + # Aliases; of course, the native symbol is included in known aliases |
| 325 | + currencies_alias[alias.symbol] = known |
| 326 | + currencies_alias[known.symbol] = known |
| 327 | + log.info( f"Currency {c}'s Proxy Symbol {alias.symbol} is an alias for {known.symbol}" ) |
337 | 328 | log.info( f"Found {len( currencies_proxy )} Invoice currency proxies: {commas( ( f'{c}: {p.symbol}' for c,p in currencies_proxy.items() ), final='and')}" ) |
338 | 329 | log.info( f"Added {len( set( currencies_account ) - currencies )} proxies: {commas( set( currencies_account ) - currencies, final='and' )}" ) |
| 330 | + log.info( f"Alias {len( currencies_alias )} symbols to their native Crytocurrencies: {commas( ( f'{a}: {c.symbol}' for a,c in currencies_alias.items() ), final='and')}" ) |
339 | 331 | currencies = set( currencies_account ) |
340 | 332 |
|
341 | 333 | # Find all LineItem.currency -> Invoice.currencies conversions required. This establishes |
@@ -406,7 +398,8 @@ def __init__( |
406 | 398 |
|
407 | 399 | self.currencies = currencies # { "USDC", "BTC", ... } |
408 | 400 | self.currencies_account = currencies_account # { "USDC": "0xaBc...12D", "BTC": "bc1...", ... } |
409 | | - self.currencies_proxy = currencies_proxy # { "BTC": TokenInfo( ... ), ... } |
| 401 | + self.currencies_proxy = currencies_proxy # { "BTC": TokenInfo( "WBTC", ... ), ... } |
| 402 | + self.currencies_alias = currencies_alias # { "WBTC": TokenInfo( "BTC", ... ), ... } |
410 | 403 | self.conversions = conversions # { ("BTC","ETH"): 14.3914, ... } |
411 | 404 |
|
412 | 405 | def headers( self ): |
@@ -597,12 +590,17 @@ def can( c ): |
597 | 590 | # {Sub}total for payment cryptocurrrencies are rounded to the individual |
598 | 591 | # Cryptocurrency's designated decimals // 3. For typical ERC-20 tokens, this is |
599 | 592 | # eg. USDC: 6 // 3 == 2, WBTC: 18 // 3 == 6. For known Cryptocurrencies, eg. BTC: 24 // |
600 | | - # 3 == 8. TODO: There should be a more sensible / less brittle way to do this. |
| 593 | + # 3 == 8. |
| 594 | + # |
| 595 | + # If a symbol is a "proxy" token for some upstream known cryptocurrency, then the |
| 596 | + # upstream native cryptocurrency's decimals should be used, so that all lines match. We |
| 597 | + # keep track of each cryptocurrency_alias[<proxy-symbol>] -> <original-symbol> |
| 598 | + # |
| 599 | + # TODO: There should be a more sensible / less brittle way to do this. |
601 | 600 | def deci( c ): |
602 | | - try: |
603 | | - return cryptocurrency_known( c ).decimals // 3 |
604 | | - except Exception: |
605 | | - return self.currencies_proxy[c].decimals // 3 |
| 601 | + if c in self.currencies_alias: |
| 602 | + return self.currencies_alias[c].decimals // 3 |
| 603 | + return self.currencies_proxy[c].decimals // 3 |
606 | 604 |
|
607 | 605 | def toti( c ): |
608 | 606 | return headers_can.index( can( f'_Total {c}' )) |
|
0 commit comments