From 288445cecc9e18115aa0e46cb6debfb6f8a13c76 Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 20 Jul 2018 16:49:45 +0200 Subject: [PATCH] tools: unified retrieval of coin data, regenerated coins_details --- defs/coins_details.json | 825 +++++++--------------------------------- tools/coin_defs.py | 404 ++++++++++++++++++++ tools/coins_details.py | 491 ++++++++++++------------ 3 files changed, 775 insertions(+), 945 deletions(-) create mode 100755 tools/coin_defs.py diff --git a/defs/coins_details.json b/defs/coins_details.json index 763c049e95..5d2abd7ea6 100644 --- a/defs/coins_details.json +++ b/defs/coins_details.json @@ -11,6 +11,21 @@ "t2_enabled": "soon", "type": "coin" }, + "coin2:CLO": { + "links": { + "Homepage": "https://callisto.network" + }, + "marketcap_usd": 0, + "name": "Callisto", + "shortcut": "CLO", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": { + "MyCrypto": "https://mycrypto.com", + "MyEtherWallet": "https://www.myetherwallet.com" + } + }, "coin2:EGEM": { "links": { "Homepage": "https://egem.io" @@ -56,6 +71,21 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, + "coin2:ESN": { + "links": { + "Homepage": "https://ethersocial.org" + }, + "marketcap_usd": 0, + "name": "Ethersocial Network", + "shortcut": "ESN", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": { + "MyCrypto": "https://mycrypto.com", + "MyEtherWallet": "https://www.myetherwallet.com" + } + }, "coin2:ETC": { "links": { "Homepage": "https://ethereumclassic.github.io" @@ -91,7 +121,7 @@ "Homepage": "https://ethereumsocial.kr" }, "marketcap_usd": 0, - "name": "EthereumSocial", + "name": "Ethereum Social", "shortcut": "ETSC", "t1_enabled": "yes", "t2_enabled": "yes", @@ -103,7 +133,7 @@ }, "coin2:EXP": { "links": { - "Homepage": "https://www.expanse.tech" + "Homepage": "https://expanse.tech" }, "marketcap_usd": 10593934, "name": "Expanse", @@ -116,11 +146,26 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, + "coin2:GO": { + "links": { + "Homepage": "https://gochain.io" + }, + "marketcap_usd": 37459560, + "name": "GoChain", + "shortcut": "GO", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": { + "MyCrypto": "https://mycrypto.com", + "MyEtherWallet": "https://www.myetherwallet.com" + } + }, "coin2:LSK": { "links": { "Homepage": "https://lisk.io/" }, - "marketcap_usd": 997454377, + "marketcap_usd": 548473107, "name": "Lisk", "shortcut": "LSK", "t1_enabled": "no", @@ -161,18 +206,19 @@ "links": { "Homepage": "https://www.stellar.org" }, - "marketcap_usd": 5331912521, + "marketcap_usd": 5315301581, "name": "Stellar", "notes": "In development", "shortcut": "XLM", "t1_enabled": "soon", - "t2_enabled": "soon" + "t2_enabled": "soon", + "type": "coin" }, "coin2:XMR": { "links": { "Homepage": "https://getmonero.org" }, - "marketcap_usd": 2831629582, + "marketcap_usd": 2215103721, "name": "Monero", "notes": "In development", "shortcut": "XMR", @@ -184,7 +230,7 @@ "links": { "Homepage": "https://ripple.com" }, - "marketcap_usd": 24313181966, + "marketcap_usd": 18016097427, "name": "Ripple", "notes": "In development", "shortcut": "XRP", @@ -203,10 +249,58 @@ "t2_enabled": "soon", "type": "coin" }, + "coin2:tETC": { + "hidden": 1, + "links": { + "Homepage": "https://ethereumclassic.github.io" + }, + "marketcap_usd": 0, + "name": "Ethereum Classic Testnet", + "shortcut": "tETC", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": { + "MyCrypto": "https://mycrypto.com", + "MyEtherWallet": "https://www.myetherwallet.com" + } + }, + "coin2:tETH": { + "hidden": 1, + "links": { + "Homepage": "https://www.ethereum.org" + }, + "marketcap_usd": 0, + "name": "Ethereum Testnet Kovan", + "shortcut": "tETH", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": { + "MyCrypto": "https://mycrypto.com", + "MyEtherWallet": "https://www.myetherwallet.com" + } + }, + "coin2:tRSK": { + "hidden": 1, + "links": { + "Homepage": "https://www.rsk.co" + }, + "marketcap_usd": 0, + "name": "RSK Testnet", + "shortcut": "tRSK", + "t1_enabled": "yes", + "t2_enabled": "yes", + "type": "coin", + "wallet": { + "MyCrypto": "https://mycrypto.com", + "MyEtherWallet": "https://www.myetherwallet.com" + } + }, "coin:BCH": { "links": { "Github": "https://github.com/Bitcoin-ABC/bitcoin-abc", - "Homepage": "https://www.bitcoincash.org/" + "Homepage": "https://www.bitcoincash.org" }, "marketcap_usd": 13729622285, "name": "Bitcoin Cash", @@ -246,7 +340,9 @@ "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", - "wallet": {} + "wallet": { + "BTCP Electrum": "https://github.com/BTCPrivate/electrum-btcp" + } }, "coin:BTG": { "links": { @@ -260,6 +356,7 @@ "t2_enabled": "yes", "type": "coin", "wallet": { + "ElectrumG": "https://github.com/BTCGPU/electrum", "Trezor": "https://wallet.trezor.io" } }, @@ -301,6 +398,7 @@ "t2_enabled": "yes", "type": "coin", "wallet": { + "Dash Electrum": "https://electrum.dash.org", "Trezor": "https://wallet.trezor.io" } }, @@ -320,7 +418,7 @@ "coin:DGB": { "links": { "Github": "https://github.com/digibyte/digibyte", - "Homepage": "https://www.digibyte.co" + "Homepage": "https://digibyte.io" }, "marketcap_usd": 472432594, "name": "DigiByte", @@ -345,7 +443,7 @@ "coin:DOGE": { "links": { "Github": "https://github.com/dogecoin/dogecoin", - "Homepage": "https://dogecoin.com" + "Homepage": "http://dogecoin.com" }, "marketcap_usd": 416955600, "name": "Dogecoin", @@ -360,7 +458,7 @@ "coin:FJC": { "links": { "Github": "https://github.com/fujicoin/fujicoin", - "Homepage": "https://www.fujicoin.org" + "Homepage": "http://fujicoin.org" }, "marketcap_usd": 871352, "name": "Fujicoin", @@ -377,6 +475,7 @@ "Github": "https://github.com/flash-coin", "Homepage": "https://www.flashcoin.io" }, + "marketcap_usd": 6262092, "name": "Flashcoin", "shortcut": "FLASH", "t1_enabled": "no", @@ -414,8 +513,8 @@ }, "coin:KOTO": { "links": { - "Github": "https://github.com/koto-dev/koto", - "Homepage": "https://koto.cash" + "Github": "https://github.com/KotoDevelopers/koto", + "Homepage": "https://ko-to.org" }, "name": "Koto", "shortcut": "KOTO", @@ -524,6 +623,7 @@ "wallet": {} }, "coin:TBTG": { + "hidden": 1, "links": { "Github": "https://github.com/BTCGPU/BTCGPU", "Homepage": "https://bitcoingold.org" @@ -591,7 +691,6 @@ "wallet": {} }, "coin:VIA": { - "hidden": 1, "links": { "Github": "https://github.com/viacoin", "Homepage": "https://viacoin.org" @@ -602,7 +701,9 @@ "t1_enabled": "yes", "t2_enabled": "yes", "type": "coin", - "wallet": {} + "wallet": { + "Vialectrum": "https://vialectrum.org" + } }, "coin:VTC": { "links": { @@ -651,7 +752,7 @@ "coin:ZEC": { "links": { "Github": "https://github.com/zcash/zcash", - "Homepage": "z.cash" + "Homepage": "https://z.cash" }, "marketcap_usd": 855681961, "name": "Zcash", @@ -751,23 +852,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:etc:PLAY": { - "address": "0x5acE17f87c7391E5792a7683069A8025B83bbd85", - "links": { - "Homepage": "http://smartbillions.com/" - }, - "marketcap_usd": 0, - "name": "Smart Billions", - "network": "etc", - "shortcut": "PLAY", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:$FFC": { "address": "0x4E84E9e5fb0A972628Cf4568c403167EF1D40431", "links": { @@ -1404,9 +1488,9 @@ "Homepage": "https://arcade.city" }, "marketcap_usd": 0, - "name": "Arcade", + "name": "ARC", "network": "eth", - "shortcut": "ARCD", + "shortcut": "ARC", "t1_enabled": "yes", "t2_enabled": "yes", "type": "erc20", @@ -2790,24 +2874,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:CARD": { - "address": "0xB07ec2c28834B889b1CE527Ca0F19364cD38935c", - "links": { - "Github": "https://github.com/cardstack", - "Homepage": "https://cardstack.com" - }, - "marketcap_usd": 0, - "name": "Cardstack Token", - "network": "eth", - "shortcut": "CARD", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:CARE": { "address": "0xbF18F246B9301F231e9561B35A3879769BB46375", "links": { @@ -3032,23 +3098,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:CDX": { - "address": "0x6fFF3806Bbac52A20e0d79BC538d527f6a22c96b", - "links": { - "Homepage": "https://commodityadnetwork.com" - }, - "marketcap_usd": 0, - "name": "CDX", - "network": "eth", - "shortcut": "CDX", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:CEEK": { "address": "0xb056c38f6b7Dc4064367403E26424CD2c60655e1", "links": { @@ -3432,23 +3481,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:COSS": { - "address": "0x65292EeadF1426Cd2dF1C4793a3d7519f253913b", - "links": { - "Homepage": "https://coss.io" - }, - "marketcap_usd": 10338104, - "name": "Coss Token", - "network": "eth", - "shortcut": "COSS", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:COV": { "address": "0xE2FB6529EF566a080e6d23dE0bd351311087D567", "links": { @@ -4206,7 +4238,7 @@ "Homepage": "https://digix.global" }, "marketcap_usd": 2032107, - "name": "DGX", + "name": "Digix Gold Token", "network": "eth", "shortcut": "DGX", "t1_enabled": "yes", @@ -4370,23 +4402,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:DOW": { - "address": "0x76974C7B79dC8a6a109Fd71fd7cEb9E40eff5382", - "links": { - "Homepage": "https://dowcoin.io/" - }, - "marketcap_usd": 0, - "name": "DOW", - "network": "eth", - "shortcut": "DOW", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:DPP": { "address": "0x01b3Ec4aAe1B8729529BEB4965F27d008788B0EB", "links": { @@ -4456,23 +4471,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:DRP": { - "address": "0x2799D90C6d44Cb9Aa5fBC377177F16C33E056b82", - "links": { - "Homepage": "http://drpcoin.com" - }, - "marketcap_usd": 0, - "name": "Dripcoin", - "network": "eth", - "shortcut": "DRP", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:DRVH": { "address": "0x62D4c04644314F35868Ba4c65cc27a77681dE7a9", "links": { @@ -4595,24 +4593,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:DUBI": { - "address": "0xD4CffeeF10F60eCA581b5E1146B5Aca4194a4C3b", - "links": { - "Github": "https://github.com/nionis/purpose", - "Homepage": "https://prps.io" - }, - "marketcap_usd": 0, - "name": "Decentralized Universal Basic Income", - "network": "eth", - "shortcut": "DUBI", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:Devcon2 Token": { "address": "0xdd94De9cFE063577051A5eb7465D08317d8808B6", "links": { @@ -4749,24 +4729,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:EDU": { - "address": "0x5b26C5D0772E5bbaC8b3182AE9a13f9BB2D03765", - "links": { - "Github": "https://github.com/livecodingtvofficial", - "Homepage": "https://tokensale.liveedu.tv" - }, - "marketcap_usd": 0, - "name": "EDU", - "network": "eth", - "shortcut": "EDU", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:EGT": { "address": "0x8e1b448EC7aDFc7Fa35FC2e885678bD323176E34", "links": { @@ -5017,7 +4979,6 @@ }, "erc20:eth:EOS": { "address": "0x86Fa049857E0209aa7D9e616F7eb3b3B78ECfdb0", - "hidden": 1, "links": { "Homepage": "https://eos.io" }, @@ -5228,12 +5189,10 @@ }, "erc20:eth:E\u20b9": { "address": "0xb67734521eAbBE9C773729dB73E16CC2dfb20A58", - "hidden": 1, "links": { "Github": "https://github.com/eRupee", "Homepage": "https://erupee.wordpress.com" }, - "marketcap_usd": 0, "name": "eRupee", "network": "eth", "shortcut": "E\u20b9", @@ -5331,23 +5290,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:FLMC": { - "address": "0x04cC783b450b8D11F3C7d00DD03fDF7FB51fE9F2", - "links": { - "Homepage": "https://filmscoin.com" - }, - "marketcap_usd": 0, - "name": "Filmscoin", - "network": "eth", - "shortcut": "FLMC", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:FLP": { "address": "0x3a1Bda28AdB5B0a812a7CF10A1950c920F79BcD3", "links": { @@ -5504,23 +5446,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:FUCK": { - "address": "0xAb16E0d25c06CB376259cc18C1de4ACA57605589", - "links": { - "Homepage": "http://fucktoken.io" - }, - "marketcap_usd": 0, - "name": "Finally Usable Crypto Karma", - "network": "eth", - "shortcut": "FUCK", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:FUEL": { "address": "0xEA38eAa3C86c8F9B751533Ba2E562deb9acDED40", "links": { @@ -5608,24 +5533,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:GANA": { - "address": "0x6754e21b9EAa053c62d7854dD6561ae451B0cBCf", - "links": { - "Github": "https://github.com/GanaProject", - "Homepage": "https://ganacoin.io" - }, - "marketcap_usd": 0, - "name": "GANA", - "network": "eth", - "shortcut": "GANA", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:GAVEL": { "address": "0x708876f486e448Ee89eB332bFbC8E593553058b9", "links": { @@ -5977,24 +5884,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:GUP": { - "address": "0xf7B098298f7C69Fc14610bf71d5e02c60792894C", - "links": { - "Github": "https://github.com/Matchpool/", - "Homepage": "https://github.com/Matchpool/" - }, - "marketcap_usd": 0, - "name": "GUP", - "network": "eth", - "shortcut": "GUP", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:GVT": { "address": "0x103c3A209da59d3E7C4A89307e66521e081CFDF0", "links": { @@ -6258,24 +6147,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:HOT": { - "address": "0x9AF839687F6C94542ac5ece2e317dAAE355493A1", - "links": { - "Github": "https://github.com/Holo-Host", - "Homepage": "https://thehydrofoundation.com/" - }, - "marketcap_usd": 21011913, - "name": "Hydro Protocol", - "network": "eth", - "shortcut": "HOT", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:HST": { "address": "0x554C20B7c486beeE439277b4540A434566dC4C02", "links": { @@ -6349,12 +6220,10 @@ }, "erc20:eth:Hdp.\u0444": { "address": "0x84543F868eC1b1FAC510d49d13C069f64cD2d5f9", - "hidden": 1, "links": { "Github": "https://github.com/HEDPAY", "Homepage": "http://hedpay.com" }, - "marketcap_usd": 0, "name": "HEdpAY", "network": "eth", "shortcut": "Hdp.\u0444", @@ -6812,23 +6681,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:JBX": { - "address": "0x884e3902C4d5cFA86de4aCE7A96AA91EbC25C0Ff", - "links": { - "Homepage": "https://www.jboxcoin.org" - }, - "marketcap_usd": 0, - "name": "JBX", - "network": "eth", - "shortcut": "JBX", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:JET": { "address": "0x8727c112C712c4a03371AC87a74dD6aB104Af768", "links": { @@ -6883,9 +6735,8 @@ }, "erc20:eth:JetCoins": { "address": "0x773450335eD4ec3DB45aF74f34F2c85348645D39", - "links": { - "Homepage": "http://www.jetcoins.trade/" - }, + "hidden": 1, + "links": {}, "marketcap_usd": 0, "name": "JetCoins", "network": "eth", @@ -6898,23 +6749,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:KC": { - "address": "0x0D6DD9f68d24EC1d5fE2174f3EC8DAB52B52BaF5", - "links": { - "Homepage": "https://www.kmcc.io" - }, - "marketcap_usd": 0, - "name": "KMCC", - "network": "eth", - "shortcut": "KC", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:KEE": { "address": "0x72D32ac1c5E66BfC5b08806271f8eEF915545164", "hidden": 1, @@ -6931,24 +6765,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:KEY": { - "address": "0x4CC19356f2D37338b9802aa8E8fc58B0373296E7", - "links": { - "Github": "https://github.com/bihu-id", - "Homepage": "https://selfkey.org" - }, - "marketcap_usd": 25785161, - "name": "SelfKey", - "network": "eth", - "shortcut": "KEY", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:KICK": { "address": "0x27695E09149AdC738A978e9A678F99E4c39e9eb9", "links": { @@ -7108,24 +6924,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:LEMO": { - "address": "0x60C24407d01782C2175D32fe7C8921ed732371D1", - "links": { - "Github": "https://github.com/LemoFoundationLtd", - "Homepage": "http://www.lemochain.com" - }, - "marketcap_usd": 0, - "name": "Lemo", - "network": "eth", - "shortcut": "LEMO", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:LEND": { "address": "0x80fB784B7eD66730e8b1DBd9820aFD29931aab03", "hidden": 1, @@ -7684,9 +7482,8 @@ }, "erc20:eth:MCAP": { "address": "0x93E682107d1E9defB0b5ee701C71707a4B2E46Bc", - "links": { - "Homepage": "https://www.mcaplabs.com/" - }, + "hidden": 1, + "links": {}, "marketcap_usd": 352675, "name": "MCAP", "network": "eth", @@ -8482,24 +8279,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:NONE": { - "address": "0x643B6870beabee941B9260a0A878bcF4A61Fb0f1", - "hidden": 1, - "links": { - "Github": "https://github.com/walleth/contracts/tree/master/NoneToken" - }, - "marketcap_usd": 0, - "name": "None", - "network": "eth", - "shortcut": "NONE", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:NOX": { "address": "0xeC46f8207D766012454c408De210BCBc2243E71c", "links": { @@ -8674,24 +8453,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:OHNI": { - "address": "0x6f539a9456A5BCb6334A1A41207c3788f5825207", - "links": { - "Github": "ohnicoin", - "Homepage": "http://ohni.us" - }, - "marketcap_usd": 0, - "name": "Ohni", - "network": "eth", - "shortcut": "OHNI", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:OJX": { "address": "0xBeef546ac8a4e0a80DC1E2d696968Ef54138f1d4", "links": { @@ -8797,24 +8558,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:OPEN": { - "address": "0x69c4BB240cF05D51eeab6985Bab35527d04a8C64", - "links": { - "Github": "https://github.com/OpenMoneyDigital", - "Homepage": "https://openfuture.io" - }, - "marketcap_usd": 0, - "name": "OPEN", - "network": "eth", - "shortcut": "OPEN", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:OPT": { "address": "0x4355fC160f74328f9b383dF2EC589bB3dFd82Ba0", "links": { @@ -9200,23 +8943,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:PLAY": { - "address": "0xE477292f1B3268687A29376116B0ED27A9c76170", - "links": { - "Homepage": "http://www.herocoin.io" - }, - "marketcap_usd": 1954962, - "name": "HeroCoin", - "network": "eth", - "shortcut": "PLAY", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:PLBT": { "address": "0x0AfFa06e7Fbe5bC9a764C979aA66E8256A631f02", "links": { @@ -9510,24 +9236,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:PRPS": { - "address": "0x7641b2Ca9DDD58adDf6e3381c1F994Aac5f1A32f", - "links": { - "Github": "https://github.com/nionis/purpose", - "Homepage": "https://prps.io" - }, - "marketcap_usd": 0, - "name": "Purpose", - "network": "eth", - "shortcut": "PRPS", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:PRS": { "address": "0x163733bcc28dbf26B41a8CfA83e369b5B3af741b", "links": { @@ -9915,24 +9623,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:RDN": { - "address": "0x255Aa6DF07540Cb5d3d297f0D0D4D84cb52bc8e6", - "links": { - "Github": "https://github.com/raiden-network/raiden/", - "Homepage": "https://raiden.network" - }, - "marketcap_usd": 0, - "name": "Raiden Network", - "network": "eth", - "shortcut": "RDN", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:REA": { "address": "0x767bA2915EC344015a7938E3eEDfeC2785195D05", "links": { @@ -10003,23 +9693,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:REP": { - "address": "0x1985365e9f78359a9B6AD760e32412f4a445E862", - "links": { - "Homepage": "https://augur.net" - }, - "marketcap_usd": 326795700, - "name": "Augur", - "network": "eth", - "shortcut": "REP", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:REQ": { "address": "0x8f8221aFbB33998d8584A2B05749bA73c37a938a", "links": { @@ -10229,9 +9902,8 @@ }, "erc20:eth:ROUND": { "address": "0x4993CB95c7443bdC06155c5f5688Be9D8f6999a5", - "links": { - "Homepage": "http://roundcoin.org/" - }, + "hidden": 1, + "links": {}, "marketcap_usd": 0, "name": "ROUND", "network": "eth", @@ -10484,23 +10156,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:SGT": { - "address": "0xd248B0D48E44aaF9c49aea0312be7E13a6dc1468", - "links": { - "Homepage": "https://sgt.selfieyo.com" - }, - "marketcap_usd": 0, - "name": "SGT", - "network": "eth", - "shortcut": "SGT", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:SHIT": { "address": "0xEF2E9966eb61BB494E5375d5Df8d67B7dB8A780D", "links": { @@ -10605,24 +10260,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:SKRP": { - "address": "0x6E34d8d84764D40f6D7b39cd569Fd017bF53177D", - "links": { - "Github": "https://github.com/SkrapsIO", - "Homepage": "https://skraps.io" - }, - "marketcap_usd": 0, - "name": "Skraps", - "network": "eth", - "shortcut": "SKRP", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:SLT": { "address": "0x7A5fF295Dc8239d5C2374E4D894202aAF029Cab6", "links": { @@ -10675,24 +10312,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:SMT": { - "address": "0x2dCFAAc11c9EebD8C6C42103Fe9e2a6AD237aF27", - "links": { - "Github": "https://github.com/SmartMeshFoundation", - "Homepage": "http://smartnode.org" - }, - "marketcap_usd": 51274091, - "name": "SmartMesh", - "network": "eth", - "shortcut": "SMT", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:SNC": { "address": "0xF4134146AF2d511Dd5EA8cDB1C4AC88C57D60404", "links": { @@ -10951,24 +10570,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:SS": { - "address": "0xbbFF862d906E348E9946Bfb2132ecB157Da3D4b4", - "links": { - "Github": "https://github.com/Sharders", - "Homepage": "https://sharder.org" - }, - "marketcap_usd": 6190295, - "name": "Sharder", - "network": "eth", - "shortcut": "SS", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:SSH": { "address": "0x6e2050CBFB3eD8A4d39b64cC9f47E711a03a5a89", "links": { @@ -11697,24 +11298,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:UMKA": { - "address": "0x8e5afc69f6227A3ad75eD346c8723Bc62ce97123", - "links": { - "Github": "https://github.com/UMKAman", - "Homepage": "https://umka.city" - }, - "marketcap_usd": 0, - "name": "UMKA", - "network": "eth", - "shortcut": "UMKA", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:UQC": { "address": "0xD01DB73E047855Efb414e6202098C4Be4Cd2423B", "links": { @@ -11955,24 +11538,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:VIBEX": { - "address": "0xe8Ff5C9c75dEb346acAc493C463C8950Be03Dfba", - "links": { - "Github": "https://github.com/amack2u/VibeHub", - "Homepage": "http://vibehub.io" - }, - "marketcap_usd": 0, - "name": "VIBEX", - "network": "eth", - "shortcut": "VIBEX", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:VIEW": { "address": "0xF03f8D65BaFA598611C3495124093c56e8F638f0", "links": { @@ -12033,7 +11598,7 @@ "Homepage": "https://vetri.global/" }, "marketcap_usd": 0, - "name": "VLD", + "name": "VETRI", "network": "eth", "shortcut": "VLD", "t1_enabled": "yes", @@ -12218,24 +11783,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:WHO": { - "address": "0xe200641890772FCe8eE6EDc5354cCEa30ac92F49", - "links": { - "Github": "https://github.com/chrisbsd/whohas", - "Homepage": "https://whohas.io" - }, - "marketcap_usd": 0, - "name": "WhoHas", - "network": "eth", - "shortcut": "WHO", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:WIC": { "address": "0x62CD07D414Ec50B68C7EcAa863a23d344f2d062f", "links": { @@ -12289,23 +11836,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:eth:WOLK": { - "address": "0x728781E75735dc0962Df3a51d7Ef47E798A7107E", - "links": { - "Homepage": "https://www.wolk.com" - }, - "marketcap_usd": 0, - "name": "Wolk Token", - "network": "eth", - "shortcut": "WOLK", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:eth:WORK": { "address": "0xD18e454D844eb0009D32E07A0Cde89E18d64CFb4", "links": { @@ -12741,7 +12271,6 @@ }, "erc20:eth:YUPIE": { "address": "0x0F33bb20a282A7649C7B3AFf644F084a9348e933", - "hidden": 1, "links": { "Github": "https://github.com/crowdholdingico/YupieSmartContract", "Homepage": "https://www.crowdholding.com" @@ -13037,22 +12566,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:kov:GUP": { - "address": "0x3C67f7D4decF7795225f51b54134F81137385f83", - "hidden": 1, - "links": {}, - "marketcap_usd": 0, - "name": "GUP", - "network": "kov", - "shortcut": "GUP", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:rin:AETH": { "address": "0x398A7A69f3c59181A1ffe34bed11DCb5DF863A8a", "links": { @@ -13106,41 +12619,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:rin:KC": { - "address": "0x275A5B346599b56917e7B1C9de019DCf9EaD861a", - "links": { - "Homepage": "https://baseblock.io/" - }, - "marketcap_usd": 0, - "name": "Karma Token", - "network": "rin", - "shortcut": "KC", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, - "erc20:rin:NONE": { - "address": "0x6475A7FA6Ed2D5180F0e0a07c2d951D12C0EDB91", - "hidden": 1, - "links": { - "Github": "https://github.com/walleth/contracts/tree/master/NoneToken" - }, - "marketcap_usd": 0, - "name": "None", - "network": "rin", - "shortcut": "NONE", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:rin:PPD": { "address": "0x12fE174C097F6B3e876B3b060C9061F4B9dEBB80", "hidden": 1, @@ -13159,23 +12637,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:rin:RDN": { - "address": "0x3615757011112560521536258c1E7325Ae3b48AE", - "links": { - "Homepage": "https://github.com/gnosis/dx-examples-liquidity-bots" - }, - "marketcap_usd": 0, - "name": "Raiden", - "network": "rin", - "shortcut": "RDN", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:rin:WALL": { "address": "0x0A057a87CE9C56D7e336B417c79cf30E8d27860B", "links": { @@ -13210,24 +12671,6 @@ "MyEtherWallet": "https://www.myetherwallet.com" } }, - "erc20:rop:NONE": { - "address": "0xFD5a69A1309595FF5121553F52C8A5B2B1B31031", - "hidden": 1, - "links": { - "Github": "https://github.com/walleth/contracts/tree/master/NoneToken" - }, - "marketcap_usd": 0, - "name": "None", - "network": "rop", - "shortcut": "NONE", - "t1_enabled": "yes", - "t2_enabled": "yes", - "type": "erc20", - "wallet": { - "MyCrypto": "https://mycrypto.com", - "MyEtherWallet": "https://www.myetherwallet.com" - } - }, "erc20:ubq:BEER": { "address": "0xFF3bF057ADF3b0E015b6465331a6236e55688274", "hidden": 1, @@ -13398,11 +12841,11 @@ } }, "info": { - "marketcap_usd": 219948273618, - "t1_coins": 690, - "t2_coins": 689, - "total_marketcap_usd": 283970152150, - "updated_at": 1532098768, - "updated_at_readable": "Fri Jul 20 16:59:28 2018" + "marketcap_usd": 219093401682, + "t1_coins": 700, + "t2_coins": 696, + "total_marketcap_usd": 284131879229, + "updated_at": 1532099100, + "updated_at_readable": "Fri Jul 20 17:05:00 2018" } } \ No newline at end of file diff --git a/tools/coin_defs.py b/tools/coin_defs.py new file mode 100755 index 0000000000..0f320f430c --- /dev/null +++ b/tools/coin_defs.py @@ -0,0 +1,404 @@ +#!/usr/bin/env python3 +from binascii import unhexlify +from collections import defaultdict, OrderedDict +import re +import os +import json +import glob +import logging + +try: + import requests +except ImportError: + requests = None + +log = logging.getLogger(__name__) + +DEFS_DIR = os.path.abspath( + os.environ.get("DEFS_DIR") or os.path.join(os.path.dirname(__file__), "..", "defs") +) + + +def load_json(*path): + if len(path) == 1 and path[0].startswith("/"): + filename = path[0] + else: + filename = os.path.join(DEFS_DIR, *path) + + with open(filename) as f: + return json.load(f, object_pairs_hook=OrderedDict) + + +# ====== coin validation ====== + + +def check_type(val, types, nullable=False, empty=False, regex=None, choice=None): + # check nullable + if val is None: + if nullable: + return + else: + raise ValueError("Missing required value") + + # check type + if not isinstance(val, types): + raise TypeError("Wrong type (expected: {})".format(types)) + + # check empty + if isinstance(val, (list, dict)) and not empty and not val: + raise ValueError("Empty collection") + + # check regex + if regex is not None: + if types is not str: + raise TypeError("Wrong type for regex check") + if not re.search(regex, val): + raise ValueError("Value does not match regex {}".format(regex)) + + # check choice + if choice is not None and val not in choice: + raise ValueError("Value not allowed, use one of: {}".format(", ".join(choice))) + + +def check_key(key, types, **kwargs): + def do_check(coin): + if not key in coin: + raise KeyError("{}: Missing key".format(key)) + try: + check_type(coin[key], types, **kwargs) + except Exception as e: + raise ValueError("{}: {}".format(key, e)) from e + + return do_check + + +COIN_CHECKS = [ + check_key("coin_name", str, regex=r"^[A-Z]"), + check_key("coin_shortcut", str, regex=r"^t?[A-Z]{3,}$"), + check_key("coin_label", str, regex=r"^[A-Z]"), + check_key("website", str, regex=r"^http.*[^/]$"), + check_key("github", str, regex=r"^https://github.com/.*[^/]$"), + check_key("maintainer", str), + check_key( + "curve_name", str, choice=["secp256k1", "secp256k1_decred", "secp256k1_groestl"] + ), + check_key("address_type", int), + check_key("address_type_p2sh", int), + check_key("maxfee_kb", int), + check_key("minfee_kb", int), + check_key("hash_genesis_block", str, regex=r"^[0-9a-f]{64}$"), + check_key("xprv_magic", int), + check_key("xpub_magic", int), + check_key("xpub_magic_segwit_p2sh", int, nullable=True), + check_key("xpub_magic_segwit_native", int, nullable=True), + check_key("slip44", int), + check_key("segwit", bool), + check_key("decred", bool), + check_key("fork_id", int, nullable=True), + check_key("force_bip143", bool), + check_key("bip115", bool), + check_key("version_group_id", int, nullable=True), + check_key("default_fee_b", dict), + check_key("dust_limit", int), + check_key("blocktime_seconds", int), + check_key("signed_message_header", str), + check_key("address_prefix", str, regex=r":$"), + check_key("min_address_length", int), + check_key("max_address_length", int), + check_key("bech32_prefix", str, nullable=True), + check_key("cashaddr_prefix", str, nullable=True), + check_key("bitcore", list, empty=True), + check_key("blockbook", list, empty=True), +] + + +def validate_coin(coin): + errors = [] + for check in COIN_CHECKS: + try: + check(coin) + except Exception as e: + errors.append(str(e)) + + magics = [ + coin[k] + for k in ( + "xprv_magic", + "xpub_magic", + "xpub_magic_segwit_p2sh", + "xpub_magic_segwit_native", + ) + if coin[k] is not None + ] + # each of those must be unique + # therefore length of list == length of set of unique values + if len(magics) != len(set(magics)): + errors.append("XPUB/XPRV magic numbers must be unique") + + if coin["address_type"] == coin["address_type_p2sh"]: + errors.append("address_type must be distinct from address_type_p2sh") + + if not coin["maxfee_kb"] >= coin["minfee_kb"]: + errors.append("max fee must not be smaller than min fee") + + if not coin["max_address_length"] >= coin["min_address_length"]: + errors.append("max address length must not be smaller than min address length") + + for bc in coin["bitcore"] + coin["blockbook"]: + if bc.endswith("/"): + errors.append("make sure URLs don't end with '/'") + + return errors + + +# ======= Coin json loaders ======= + + +def get_coins(): + coins = [] + for filename in glob.glob(os.path.join(DEFS_DIR, "coins", "*.json")): + coin = load_json(filename) + coin.update( + name=coin["coin_name"], + shortcut=coin["coin_shortcut"], + key="btc:{}".format(coin["coin_shortcut"]), + ) + coins.append(coin) + + return coins + + +def get_ethereum_networks(): + networks = load_json("ethereum", "networks.json") + for network in networks: + network.update(key="eth:{}".format(network["shortcut"])) + return networks + + +def get_erc20_tokens(): + networks = get_ethereum_networks() + tokens = [] + for network in networks: + if network["name"].startswith("Ethereum Testnet "): + idx = len("Ethereum Testnet ") + chain = network["name"][idx : idx + 3] + else: + chain = network["shortcut"] + chain = chain.lower() + if not chain: + continue + + chain_path = os.path.join(DEFS_DIR, "ethereum", "tokens", "tokens", chain) + for filename in glob.glob(os.path.join(chain_path, "*.json")): + token = load_json(filename) + token.update( + chain=chain, + chain_id=network["chain_id"], + address_bytes=unhexlify(token["address"][2:]), + shortcut=token["symbol"], + key="erc20:{}:{}".format(chain, token["symbol"]), + ) + tokens.append(token) + + return tokens + + +def get_nem_mosaics(): + mosaics = load_json("nem", "nem_mosaics.json") + for mosaic in mosaics: + shortcut = mosaic["ticker"].strip() + mosaic.update(shortcut=shortcut, key="nem:{}".format(shortcut)) + return mosaics + + +def get_others(): + others = load_json("others.json") + for other in others: + other.update(key="network:{}".format(other["shortcut"])) + return others + + +# ====== support info ====== + +RELEASES_URL = "https://wallet.trezor.io/data/firmware/{}/releases.json" +ETHEREUM_TOKENS = { + "1": "https://raw.githubusercontent.com/trezor/trezor-mcu/v{}/firmware/ethereum_tokens.c", + "2": "https://raw.githubusercontent.com/trezor/trezor-core/v{}/src/apps/ethereum/tokens.py", +} + +TOKEN_MATCH = { + "1": re.compile(r'\{.*" ([^"]+)".*\},'), + "2": re.compile(r'\(.*["\']([^"\']+)["\'].*\),'), +} + + +def latest_releases(): + if not requests: + raise RuntimeError("requests library is required for getting release info") + + latest = {} + for v in ("1", "2"): + releases = requests.get(RELEASES_URL.format(v)).json() + latest[v] = max(tuple(r["version"]) for r in releases) + return latest + + +def support_info_erc20(coins, versions): + supported_tokens = {} + for trezor, version in versions.items(): + tokens = set() + version_str = ".".join(map(str, version)) + + token_file = requests.get(ETHEREUM_TOKENS[trezor].format(version_str)).text + token_match = TOKEN_MATCH[trezor] + + for line in token_file.split("\n"): + m = token_match.search(line) + if m: + tokens.add(m.group(1)) + + supported_tokens[trezor] = tokens + + support = {} + for coin in coins: + key = coin["key"] + if not key.startswith("erc20:"): + continue + + support[key] = dict( + trezor1="yes" if coin["shortcut"] in supported_tokens["1"] else "soon", + trezor2="yes" if coin["shortcut"] in supported_tokens["2"] else "soon", + ) + + return support + + +def support_info(coins, erc20_versions=None): + support_data = load_json("support.json") + support = {} + for coin in coins: + key = coin["key"] + typ = key.split(":", maxsplit=1)[0] + if key in support_data: + support[key] = support_data[key] + + elif typ in ("nem", "eth"): + support[key] = dict(trezor1="yes", trezor2="yes") + + elif typ == "erc20": + # you must call a separate function to get that + continue + + else: + log.warning("support info missing for {}".format(key)) + support[key] = {} + + if erc20_versions: + erc20 = support_info_erc20(coins, erc20_versions) + erc20.update(support) + return erc20 + else: + return support + + +# ====== data cleanup functions ====== + + +def find_address_collisions(coins): + slip44 = defaultdict(list) + at_p2pkh = defaultdict(list) + at_p2sh = defaultdict(list) + + for name, coin in coins.items(): + s = coin["slip44"] + # ignore m/1 testnets + if not (name.endswith("Testnet") and s == 1): + slip44[s].append(s) + + # skip address types on cashaddr currencies + if coin["cashaddr_prefix"]: + continue + + at_p2pkh[coin["address_type"]].append(name) + at_p2sh[coin["address_type_p2sh"]].append(name) + + def prune(d): + ret = d.copy() + for key in d.keys(): + if len(d[key]) < 2: + del ret[key] + return ret + + return dict( + slip44=prune(slip44), + address_type=prune(at_p2pkh), + address_type_p2sh=prune(at_p2sh), + ) + + +def ensure_mandatory_values(coins): + for coin in coins: + if not all(coin.get(k) for k in ("name", "shortcut", "key")): + raise ValueError(coin) + + +def filter_duplicate_shortcuts(coins): + dup_keys = set() + retained_coins = OrderedDict() + + for coin in coins: + key = coin["shortcut"] + if key in dup_keys: + pass + elif key in retained_coins: + dup_keys.add(key) + del retained_coins[key] + else: + retained_coins[key] = coin + + # modify original list + coins[:] = retained_coins.values() + # return duplicates + return dup_keys + + +def _btc_sort_key(coin): + if coin["name"] in ("Bitcoin", "Testnet"): + return "000000" + coin["name"] + else: + return coin["name"] + + +def get_all(): + all_coins = dict( + btc=get_coins(), + eth=get_ethereum_networks(), + erc20=get_erc20_tokens(), + nem=get_nem_mosaics(), + other=get_others(), + ) + + for k, coins in all_coins.items(): + if k == "btc": + coins.sort(key=_btc_sort_key) + else: + coins.sort(key=lambda c: c["key"].upper()) + + ensure_mandatory_values(coins) + if k != "eth": + dup_keys = filter_duplicate_shortcuts(coins) + if dup_keys: + log.warning( + "{}: removing duplicate symbols: {}".format(k, ", ".join(dup_keys)) + ) + + return all_coins + + +def get_list(): + all_coins = get_all() + return sum(all_coins.values(), []) + + +def get_dict(): + return {coin["key"]: coin for coin in get_list()} diff --git a/tools/coins_details.py b/tools/coins_details.py index 5438f3823e..c00111945a 100755 --- a/tools/coins_details.py +++ b/tools/coins_details.py @@ -2,360 +2,343 @@ """Fetch information about coins and tokens supported by Trezor and update it in coins_details.json.""" import time import json +import logging import requests -import ethereum_tokens_gen -import build_coins -from distutils.version import LooseVersion +import coin_defs -T1_LATEST='1.6.2' -T2_LATEST='2.0.7' +OPTIONAL_KEYS = ("links", "notes", "wallet") +OVERRIDES = coin_defs.load_json("coins_details.override.json") + +VERSIONS = coin_defs.latest_releases() COINS = {} +log = logging.getLogger(__name__) + + def coinmarketcap_init(): global COINS try: - COINS = json.load(open('coinmarketcap.json', 'r')) + marketcap_json = json.load(open("coinmarketcap.json", "r")) except FileNotFoundError: pass else: - if COINS["1"]["last_updated"] > time.time() - 3600: - print("Using local cache of coinmarketcap") - return + pass + # if COINS["1"]["last_updated"] > time.time() - 3600: + # print("Using local cache of coinmarketcap") + # return + + for coin in marketcap_json.values(): + slug = coin["website_slug"] + market_cap = coin["quotes"]["USD"]["market_cap"] + if market_cap is not None: + COINS[slug] = int(float(market_cap)) + + return print("Updating coins from coinmarketcap") total = None COINS = {} while total is None or len(COINS) < total: - url = 'https://api.coinmarketcap.com/v2/ticker/?start=%d&convert=USD&limit=100' % (len(COINS)) + url = ( + "https://api.coinmarketcap.com/v2/ticker/?start=%d&convert=USD&limit=100" + % (len(COINS) + 1) + ) data = requests.get(url).json() - COINS.update(data['data']) + COINS.update(data["data"]) if total is None: - total = data['metadata']['num_cryptocurrencies'] + total = data["metadata"]["num_cryptocurrencies"] print("Fetched %d of %d coins" % (len(COINS), total)) time.sleep(1) - json.dump(COINS, open('coinmarketcap.json', 'w'), sort_keys=True, indent=4) + json.dump(COINS, open("coinmarketcap.json", "w"), sort_keys=True, indent=4) -def coinmarketcap_info(shortcut): - global COINS - shortcut = shortcut.replace(' ', '-').lower() +def marketcap(coin): + cap = None - for _id in COINS: - coin = COINS[_id] - # print(shortcut, coin['website_slug']) - if shortcut == coin['website_slug']: - # print(coin) - return coin + if "coinmarketcap_alias" in coin: + cap = COINS.get(coin["coinmarketcap_alias"]) + + if not cap: + slug = coin["name"].replace(" ", "-").lower() + cap = COINS.get(slug) + + if not cap: + cap = COINS.get(coin["shortcut"].lower()) + + return cap -def update_marketcap(obj, *shortcuts): - for sym in shortcuts: - try: - obj['marketcap_usd'] = int(float(coinmarketcap_info(sym)['quotes']['USD']['market_cap'])) - return - except: - pass - # print("Marketcap info not found for", shortcut) +def update_marketcap(coins): + for coin in coins.values(): + cap = marketcap(coin) + if cap: + coin["marketcap_usd"] = cap def coinmarketcap_global(): - url = 'https://api.coinmarketcap.com/v2/global' + url = "https://api.coinmarketcap.com/v2/global" ret = requests.get(url) data = ret.json() return data -def set_default(obj, key, default_value): - obj[key] = obj.setdefault(key, default_value) - - def update_info(details): - details['info']['updated_at'] = int(time.time()) - details['info']['updated_at_readable'] = time.asctime() + details["info"]["updated_at"] = int(time.time()) + details["info"]["updated_at_readable"] = time.asctime() - details['info']['t1_coins'] = len([True for _, c in details['coins'].items() if c.get('t1_enabled') == 'yes' and not c.get('hidden', False)]) - details['info']['t2_coins'] = len([True for _, c in details['coins'].items() if c.get('t2_enabled') == 'yes' and not c.get('hidden', False)]) + details["info"]["t1_coins"] = len( + [ + True + for _, c in details["coins"].items() + if c.get("t1_enabled") == "yes" and not c.get("hidden", False) + ] + ) + details["info"]["t2_coins"] = len( + [ + True + for _, c in details["coins"].items() + if c.get("t2_enabled") == "yes" and not c.get("hidden", False) + ] + ) try: - details['info']['total_marketcap_usd'] = int(coinmarketcap_global()['data']['quotes']['USD']['total_market_cap']) + details["info"]["total_marketcap_usd"] = int( + coinmarketcap_global()["data"]["quotes"]["USD"]["total_market_cap"] + ) except: pass marketcap = 0 - for k, c in details['coins'].items(): - if c['t1_enabled'] == 'yes' or c['t2_enabled'] == 'yes': - marketcap += details['coins'][k].setdefault('marketcap_usd', 0) - details['info']['marketcap_usd'] = marketcap + for k, c in details["coins"].items(): + if c["t1_enabled"] == "yes" or c["t2_enabled"] == "yes": + marketcap += details["coins"][k].setdefault("marketcap_usd", 0) + details["info"]["marketcap_usd"] = marketcap def check_unsupported(details, prefix, supported): - for k in details['coins'].keys(): + for k in details["coins"].keys(): if not k.startswith(prefix): continue if k not in supported: print("%s not supported by Trezor? (Possible manual entry)" % k) -def update_coins(details): - (coins, _) = build_coins.process(None) - firmware = json.load(open('../defs/support.json', 'r')) +def _is_supported(support, trezor_version): + version = VERSIONS[str(trezor_version)] + nominal = support.get("trezor" + str(trezor_version)) + if nominal is None: + return "no" + elif isinstance(nominal, bool): + return "yes" if nominal else "no" - supported = [] - for key, coin in coins.items(): - - t1_enabled = 'no' - if key in firmware['trezor1']: - if LooseVersion(firmware['trezor1'][key]) <= LooseVersion(T1_LATEST): - t1_enabled = 'yes' - else: - t1_enabled = 'soon' - - t2_enabled = 'no' - if key in firmware['trezor2']: - if LooseVersion(firmware['trezor2'][key]) <= LooseVersion(T2_LATEST): - t2_enabled = 'yes' - else: - t2_enabled = 'soon' - - # print("Updating", coin['coin_label'], coin['coin_shortcut']) - key = "coin:%s" % coin['coin_shortcut'] - supported.append(key) - out = details['coins'].setdefault(key, {}) - out['type'] = 'coin' - out['t1_enabled'] = t1_enabled - out['t2_enabled'] = t2_enabled - - set_default(out, 'shortcut', coin['coin_shortcut']) - set_default(out, 'name', coin['coin_label']) - set_default(out, 'links', {}) - set_default(out['links'], 'Homepage', coin['website']) - set_default(out['links'], 'Github', coin['github']) - set_default(out, 'wallet', {}) - update_marketcap(out, coin.get('coinmarketcap_alias', coin['coin_label'])) - - check_unsupported(details, 'coin:', supported) + try: + nominal_version = tuple(map(int, nominal.split("."))) + return "yes" if nominal_version <= version else "soon" + except ValueError: + return nominal -def update_erc20(details): - networks = [x[0] for x in ethereum_tokens_gen.networks] +def _webwallet_support(coin, support): + """Check the "webwallet" support property. + If set, check that at least one of the backends run on trezor.io. + If yes, assume we support the coin in our wallet. + Otherwise it's probably working with a custom backend, which means don't + link to our wallet. + """ + if not support.get("webwallet"): + return False + return any(".trezor.io" in url for url in coin["blockbook"] + coin["bitcore"]) - LATEST_T1 = 'https://raw.githubusercontent.com/trezor/trezor-mcu/v%s/firmware/ethereum_tokens.c' % T1_LATEST - LATEST_T2 = 'https://raw.githubusercontent.com/trezor/trezor-core/v%s/src/apps/ethereum/tokens.py' % T2_LATEST - tokens = ethereum_tokens_gen.get_tokens() - tokens_t1 = requests.get(LATEST_T1).text - tokens_t2 = requests.get(LATEST_T2).text +def update_coins(coins, support_info): + res = {} + for coin in coins: + key = coin["key"] + support = support_info[key] + details = dict( + type="coin", + shortcut=coin["shortcut"], + name=coin["coin_label"], + links=dict(Homepage=coin["website"], Github=coin["github"]), + t1_enabled=_is_supported(support, 1), + t2_enabled=_is_supported(support, 2), + wallet={}, + ) + if _webwallet_support(coin, support): + details["wallet"]["Trezor"] = "https://wallet.trezor.io" + if support.get("other"): + details["wallet"].update(support["other"]) - supported = [] - for t in tokens: - # print('Updating', t['symbol']) + # XXX get rid of this in favor of res[key] + res["coin:{}".format(coin["shortcut"])] = details - if t['chain'] not in networks: - print('Skipping, %s is disabled' % t['chain']) - continue + return res - key = "erc20:%s:%s" % (t['chain'], t['symbol']) - supported.append(key) - out = details['coins'].setdefault(key, {}) - out['type'] = 'erc20' - out['network'] = t['chain'] - out['address'] = t['address'] - set_default(out, 'shortcut', t['symbol']) - set_default(out, 'name', t['name']) - set_default(out, 'links', {}) - set_default(out, 'wallet', {}) +def update_erc20(coins, support_info): + # TODO skip disabled networks? + res = {} + for coin in coins: + key = coin["key"] + support = support_info[key] + details = dict( + type="erc20", + network=coin["chain"], + address=coin["address"], + shortcut=coin["shortcut"], + name=coin["name"], + links={}, + wallet=dict( + MyCrypto="https://mycrypto.com", + MyEtherWallet="https://www.myetherwallet.com", + ), + t1_enabled=support["trezor1"], + t2_enabled=support["trezor2"], + ) + if coin.get("website"): + details["links"]["Homepage"] = coin["website"] + if coin.get("social", {}).get("github"): + details["links"]["Github"] = coin["social"]["github"] - if "\" %s\"" % t['symbol'] in tokens_t1: - out['t1_enabled'] = 'yes' + res[key] = details + + return res + + +def update_simple(coins, support_info, type): + res = {} + for coin in coins: + key = coin["key"] + support = support_info[key] + + # XXX drop newkey + if type == "mosaic": + newkey = "mosaic:{}".format(coin["shortcut"]) else: - out['t1_enabled'] = 'soon' + newkey = "coin2:{}".format(coin["shortcut"]) - if "'%s'" % t['symbol'] in tokens_t2: - out['t2_enabled'] = 'yes' - else: - out['t2_enabled'] = 'soon' + details = dict( + name=coin["name"], + shortcut=coin["shortcut"], + type=type, + t1_enabled=_is_supported(support, 1), + t2_enabled=_is_supported(support, 2), + ) + for key in OPTIONAL_KEYS: + if key in coin: + details[key] = coin[key] - out['wallet']['MyCrypto'] = 'https://mycrypto.com' - out['wallet']['MyEtherWallet'] = 'https://www.myetherwallet.com' + res[newkey] = details - if t.get('website'): - out['links']['Homepage'] = t['website'] - if t.get('social', {}).get('github', None): - out['links']['Github'] = t['social']['github'] - - update_marketcap(out, out.get('coinmarketcap_alias'), t['name'], t['symbol']) - - check_unsupported(details, 'erc20:', supported) + return res -def update_ethereum(details): - out = details['coins'].setdefault('coin2:ETH', {}) - out['type'] = 'coin' - set_default(out, 'shortcut', 'ETH') - set_default(out, 'name', 'Ethereum') - set_default(out, 't1_enabled', 'yes') - set_default(out, 't2_enabled', 'yes') - update_marketcap(out, 'ethereum') - - out = details['coins'].setdefault('coin2:ETC', {}) - out['type'] = 'coin' - set_default(out, 'shortcut', 'ETC') - set_default(out, 'name', 'Ethereum Classic') - set_default(out, 't1_enabled', 'yes') - set_default(out, 't2_enabled', 'yes') - update_marketcap(out, 'ethereum-classic') - - out = details['coins'].setdefault('coin2:RSK', {}) - out['type'] = 'coin' - set_default(out, 'shortcut', 'RSK') - set_default(out, 'name', 'Rootstock') - set_default(out, 't1_enabled', 'yes') - set_default(out, 't2_enabled', 'yes') - update_marketcap(out, 'rootstock') - - out = details['coins'].setdefault('coin2:EXP', {}) - out['type'] = 'coin' - set_default(out, 'shortcut', 'EXP') - set_default(out, 'name', 'Expanse') - set_default(out, 't1_enabled', 'yes') - set_default(out, 't2_enabled', 'yes') - update_marketcap(out, 'expanse') - - out = details['coins'].setdefault('coin2:UBQ', {}) - out['type'] = 'coin' - set_default(out, 'shortcut', 'UBQ') - set_default(out, 'name', 'Ubiq') - set_default(out, 't1_enabled', 'yes') - set_default(out, 't2_enabled', 'yes') - update_marketcap(out, 'ubiq') - - out = details['coins'].setdefault('coin2:ELLA', {}) - out['type'] = 'coin' - set_default(out, 'shortcut', 'ELLA') - set_default(out, 'name', 'Ellaism') - set_default(out, 't1_enabled', 'yes') - set_default(out, 't2_enabled', 'yes') - update_marketcap(out, 'ellaism') - - out = details['coins'].setdefault('coin2:EGEM', {}) - out['type'] = 'coin' - set_default(out, 'shortcut', 'EGEM') - set_default(out, 'name', 'EtherGem') - set_default(out, 't1_enabled', 'yes') - set_default(out, 't2_enabled', 'yes') - update_marketcap(out, 'egem') - - out = details['coins'].setdefault('coin2:ETSC', {}) - out['type'] = 'coin' - set_default(out, 'shortcut', 'ETSC') - set_default(out, 'name', 'Ethereum Social') - set_default(out, 't1_enabled', 'yes') - set_default(out, 't2_enabled', 'yes') - update_marketcap(out, 'etsc') - - out = details['coins'].setdefault('coin2:EOSC', {}) - out['type'] = 'coin' - set_default(out, 'shortcut', 'EOSC') - set_default(out, 'name', 'EOS Classic') - set_default(out, 't1_enabled', 'yes') - set_default(out, 't2_enabled', 'yes') - update_marketcap(out, 'eosc') - - out = details['coins'].setdefault('coin2:ADA', {}) - out['type'] = 'coin' - set_default(out, 'shortcut', 'ADA') - set_default(out, 'name', 'Cardano') - set_default(out, 't1_enabled', 'no') - set_default(out, 't2_enabled', 'soon') - update_marketcap(out, 'cardano') - - out = details['coins'].setdefault('coin2:XTZ', {}) - out['type'] = 'coin' - set_default(out, 'shortcut', 'XTZ') - set_default(out, 'name', 'Tezos') - set_default(out, 't1_enabled', 'no') - set_default(out, 't2_enabled', 'soon') - update_marketcap(out, 'tezos') - - -def update_mosaics(details): - d = json.load(open('../defs/nem/nem_mosaics.json')) - supported = [] - for mosaic in d: - # print('Updating', mosaic['name'], mosaic['ticker']) - - key = "mosaic:%s" % mosaic['ticker'].strip() - supported.append(key) - out = details['coins'].setdefault(key, {}) - out['type'] = 'mosaic' - set_default(out, 'shortcut', mosaic['ticker'].strip()) - set_default(out, 'name', mosaic['name']) - set_default(out, 't1_enabled', 'yes') - set_default(out, 't2_enabled', 'yes') - - update_marketcap(out, out.get('coinmarketcap_alias', out['name'])) - - check_unsupported(details, 'mosaic:', supported) +def update_ethereum_networks(coins, support_info): + res = update_simple(coins, support_info, "coin") + for coin in coins: + newkey = "coin2:{}".format(coin["shortcut"]) + res[newkey]["wallet"] = dict( + MyCrypto="https://mycrypto.com", + MyEtherWallet="https://www.myetherwallet.com", + ) + res[newkey]["links"] = dict(Homepage=coin.get("url")) + return res def check_missing_details(details): - for k in details['coins'].keys(): - coin = details['coins'][k] + for k in details["coins"].keys(): + coin = details["coins"][k] hide = False - if 'links' not in coin: + if "links" not in coin: print("%s: Missing links" % k) hide = True - if 'Homepage' not in coin.get('links', {}): + if "Homepage" not in coin.get("links", {}): print("%s: Missing homepage" % k) hide = True - if coin['t1_enabled'] not in ('yes', 'no', 'planned', 'soon'): + if coin["t1_enabled"] not in ("yes", "no", "planned", "soon"): print("%s: Unknown t1_enabled" % k) hide = True - if coin['t2_enabled'] not in ('yes', 'no', 'planned', 'soon'): + if coin["t2_enabled"] not in ("yes", "no", "planned", "soon"): print("%s: Unknown t2_enabled" % k) hide = True - if 'Trezor' in coin.get('wallet', {}) and coin['wallet']['Trezor'] != 'https://wallet.trezor.io': + if ( + "Trezor" in coin.get("wallet", {}) + and coin["wallet"]["Trezor"] != "https://wallet.trezor.io" + ): print("%s: Strange URL for Trezor Wallet" % k) hide = True - if len(coin.get('wallet', {})) == 0: + if len(coin.get("wallet", {})) == 0: print("%s: Missing wallet" % k) + if "Testnet" in coin["name"]: + print("%s: Hiding testnet" % k) + hide = True + if hide: - if coin.get('hidden') != 1: + if coin.get("hidden") != 1: print("%s: HIDING COIN!" % k) # If any of important detail is missing, hide coin from list - coin['hidden'] = 1 + coin["hidden"] = 1 - if not hide and coin.get('hidden'): + if not hide and coin.get("hidden"): print("%s: Details are OK, but coin is still hidden" % k) - for k in details['coins'].keys(): - if details['coins'][k].get('hidden') == 1: + for k in details["coins"].keys(): + if details["coins"][k].get("hidden") == 1: print("%s: Coin is hidden" % k) -if __name__ == '__main__': - try: - details = json.load(open('../defs/coins_details.json', 'r')) - except FileNotFoundError: - details = {'coins': {}, 'info': {}} +def apply_overrides(coins): + for key, override in OVERRIDES.items(): + if key not in coins: + log.warning("override without coin: {}".format(key)) + continue + + def recursive_update(orig, new): + if isinstance(new, dict) and isinstance(orig, dict): + for k, v in new.items(): + orig[k] = recursive_update(orig.get(k), v) + else: + return new + + coin = coins[key] + recursive_update(coin, override) + + +if __name__ == "__main__": + # try: + # details = json.load(open('../defs/coins_details.json', 'r')) + # except FileNotFoundError: + # details = {'coins': {}, 'info': {}} coinmarketcap_init() - update_coins(details) - update_erc20(details) - update_ethereum(details) - update_mosaics(details) + + defs = coin_defs.get_all() + all_coins = sum(defs.values(), []) + support_info = coin_defs.support_info(all_coins, erc20_versions=VERSIONS) + + coins = {} + coins.update(update_coins(defs["btc"], support_info)) + coins.update(update_erc20(defs["erc20"], support_info)) + coins.update(update_ethereum_networks(defs["eth"], support_info)) + coins.update(update_simple(defs["nem"], support_info, "mosaic")) + coins.update(update_simple(defs["other"], support_info, "coin")) + + apply_overrides(coins) + update_marketcap(coins) + + details = dict(coins=coins, info={}) update_info(details) check_missing_details(details) - print(json.dumps(details['info'], sort_keys=True, indent=4)) - json.dump(details, open('../defs/coins_details.json', 'w'), sort_keys=True, indent=4) + print(json.dumps(details["info"], sort_keys=True, indent=4)) + json.dump(details, open("../defs/coins_details.json", "w"), sort_keys=True, indent=4) +