SPB Git

spb/os-vault Public

Self-custody, multi-chain crypto wallet for macOS. One recovery phrase, six chain families, zero API keys — nothing leaves your Mac.

Swift 96% Shell 3.4% Makefile 0.6%
5.3 KB · 129 lines swift
Raw Blame History
1//2//  Token.swift3//  OS Vault4//5//  Author: Simon-Pierre Boucher6//  Mail: contact@spboucher.ai7//89import Foundation1011/// An ERC-20 stablecoin deployment set. Every address and decimal count below12/// was verified live on-chain via `symbol()`/`decimals()` eth_calls13/// (docs/STABLECOINS.md + docs/RESEARCH-MULTICHAIN.md, 2026-08-05).14///15/// Registry rules learned from that research:16/// - decimals are per-DEPLOYMENT data: USDT/USDC are 18 on BNB Chain, 617///   everywhere else — hence the separate BNB-peg entries.18/// - bridged USDC.e reports the same `symbol()` as native USDC on19///   Arbitrum/Optimism/Polygon; the registry distinguishes them by address20///   and shows "USDC.e" to the user.21/// - Arbitrum/Polygon USDT was upgraded in place to USDT0 (same address,22///   same 6 decimals) — never assert on-chain symbol strings.23public struct Token: Identifiable, Hashable, Sendable {24    public let symbol: String25    public let name: String26    public let decimals: Int27    /// Checksummed contract address per network the token is deployed on.28    public let addresses: [Network: String]2930    public var id: String { symbol }3132    public func address(on network: Network) -> String? {33        addresses[network]34    }3536    // MARK: - Registry3738    public static let usdc = Token(39        symbol: "USDC", name: "USD Coin", decimals: 6,40        addresses: [41            .baseSepolia: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",42            .baseMainnet: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",43            .ethereum: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",44            .arbitrum: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",45            .optimism: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",46            .polygon: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",47            .avalanche: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",48            .linea: "0x176211869cA2b568f2A7D4EE941E073a821EE1ff"49        ]50    )5152    /// Bridged USDC — same on-chain symbol as native USDC, different address.53    public static let usdce = Token(54        symbol: "USDC.e", name: "Bridged USDC", decimals: 6,55        addresses: [56            .arbitrum: "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8",57            .optimism: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607",58            .polygon: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",59            .avalanche: "0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664",60            .gnosis: "0x2a22f9c3b484c3629090FeED35F17Ff8F88f76F0",61            .scroll: "0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4"62        ]63    )6465    public static let usdt = Token(66        symbol: "USDT", name: "Tether USD", decimals: 6,67        addresses: [68            .baseMainnet: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",69            .ethereum: "0xdAC17F958D2ee523a2206206994597C13D831ec7",70            .arbitrum: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",   // USDT0 in-place upgrade71            .optimism: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58",72            .polygon: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",    // USDT0 in-place upgrade73            .avalanche: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",  // native USDt74            .gnosis: "0x4ECaBa5870353805a9F068101A40E0f32ed605C6",75            .linea: "0xA219439258ca9da29E9Cc4cE5596924745e12B93",76            .scroll: "0xf55BEC9cafDbE8730f096Aa55dad6D22d44099Df"77        ]78    )7980    /// Binance-peg variants: 18 decimals, not issuer-native. Kept as separate81    /// registry entries because decimals differ from every other deployment.82    public static let usdtBNB = Token(83        symbol: "USDT", name: "Tether USD (BNB peg)", decimals: 18,84        addresses: [.bnb: "0x55d398326f99059fF775485246999027B3197955"]85    )8687    public static let usdcBNB = Token(88        symbol: "USDC", name: "USD Coin (BNB peg)", decimals: 18,89        addresses: [.bnb: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d"]90    )9192    public static let dai = Token(93        symbol: "DAI", name: "Dai Stablecoin", decimals: 18,94        addresses: [95            .baseMainnet: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",96            .ethereum: "0x6B175474E89094C44Da98b954EedeAC495271d0F",97            .arbitrum: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1",98            .optimism: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1",99            .polygon: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",100            .bnb: "0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3",101            .avalanche: "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70",  // DAI.e102            .linea: "0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5",103            .scroll: "0xcA77eB3fEFe3725Dc33bccB54eDEFc3D9f764f97"104        ]105    )106107    public static let usds = Token(108        symbol: "USDS", name: "Sky Dollar", decimals: 18,109        addresses: [110            .ethereum: "0xdC035D45d973E3EC169d2276DDab16f1e407384F",111            .baseMainnet: "0x820C137fa70C8691f0e44Dc420a5e53c168921Dc"112        ]113    )114115    public static let eurc = Token(116        symbol: "EURC", name: "Euro Coin", decimals: 6,117        addresses: [118            .baseMainnet: "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"119        ]120    )121122    public static let all: [Token] = [usdc, usdcBNB, usdce, usdt, usdtBNB, dai, usds, eurc]123124    /// Tokens usable on a given network, USDC-family first.125    public static func available(on network: Network) -> [Token] {126        all.filter { $0.addresses[network] != nil }127    }128}129