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%
7.8 KB · 265 lines swift
Raw Blame History
1import Foundation23// MARK: - Deprecation45@available(*, deprecated, renamed: "TokenMetadata")6public typealias Token = TokenMetadata78public extension TokenMetadata {9    @available(*, deprecated, renamed: "mintAddress")10    var address: String {11        mintAddress12    }13}1415// MARK: - TokenMetadata1617/// Common token metadata structure.18public struct TokenMetadata: Hashable, Codable, Equatable {19    public let chainId: Int20    public let mintAddress: String21    public let symbol: String22    public let name: String23    public let decimals: Decimals24    public let logoURI: String?25    public var tags: [TokenTag]26    public let extensions: [String: TokenExtensionValue]?27    public let supply: UInt64?28    public private(set) var isNative = false2930    let _tags: [String]?3132    enum CodingKeys: String, CodingKey {33        case chainId, mintAddress = "address", symbol, name, decimals, logoURI, extensions, _tags = "tags", supply34    }3536    public init(from decoder: Decoder) throws {37        let container = try decoder.container(keyedBy: CodingKeys.self)38        chainId = try container.decode(Int.self, forKey: .chainId)39        mintAddress = try container.decode(String.self, forKey: .mintAddress)40        symbol = try container.decode(String.self, forKey: .symbol)41        name = try container.decode(String.self, forKey: .name)42        decimals = try container.decode(Decimals.self, forKey: .decimals)43        logoURI = try container.decodeIfPresent(String.self, forKey: .logoURI)44        extensions = try container.decodeIfPresent([String: TokenExtensionValue].self, forKey: .extensions)45        _tags = try container.decodeIfPresent([String].self, forKey: ._tags)46        supply = try container.decodeIfPresent(UInt64.self, forKey: .supply)4748        // Customizing tags49        tags = _tags?.map { tag in TokenTag(name: tag, description: tag) } ?? []50    }5152    public init(53        tags: [String]?,54        chainId: Int,55        mintAddress: String,56        symbol: String,57        name: String,58        decimals: UInt8,59        logoURI: String?,60        extensions: [String: TokenExtensionValue]?,61        isNative: Bool = false,62        supply: UInt64? = nil63    ) {64        _tags = tags65        self.chainId = chainId66        self.mintAddress = mintAddress67        self.symbol = symbol68        self.name = name69        self.decimals = decimals70        self.logoURI = logoURI71        self.tags = tags?.map { tag in TokenTag(name: tag, description: tag) } ?? []72        self.extensions = extensions73        self.isNative = isNative74        self.supply = supply75    }7677    public static func unsupported(78        tags: [String]?,79        mint: String,80        decimals: Decimals,81        symbol: String,82        supply: UInt64?83    ) -> TokenMetadata {84        TokenMetadata(85            tags: tags,86            chainId: 101,87            mintAddress: mint,88            symbol: symbol,89            name: mint,90            decimals: decimals,91            logoURI: nil,92            extensions: nil,93            supply: supply94        )95    }9697    @available(*, deprecated, renamed: "isNative")98    public var isNativeSOL: Bool {99        isNative100    }101102    public func hasSameMintAddress(with other: TokenMetadata) -> Bool {103        mintAddress == other.mintAddress104    }105106    public var generalTokenExtensions: GeneralTokenExtension {107        GeneralTokenExtension(data: extensions ?? [:])108    }109}110111public struct TokenTag: Hashable, Codable {112    public var name: String113    public var description: String114}115116public enum TokenExtensionValue: Hashable, Codable {117    case string(String)118    case int(Int)119    case double(Double)120    case bool(Bool)121    case unknown122123    public init(from decoder: Decoder) throws {124        let container = try decoder.singleValueContainer()125126        if let value = try? container.decode(String.self) {127            self = .string(value)128        } else if let value = try? container.decode(Int.self) {129            self = .int(value)130        } else if let value = try? container.decode(Double.self) {131            self = .double(value)132        } else if let value = try? container.decode(Bool.self) {133            self = .bool(value)134        } else {135            self = .unknown136        }137    }138139    public func encode(to encoder: Encoder) throws {140        var container = encoder.singleValueContainer()141142        switch self {143        case let .string(value):144            try container.encode(value)145        case let .int(value):146            try container.encode(value)147        case let .double(value):148            try container.encode(value)149        case let .bool(value):150            try container.encode(value)151        case .unknown:152            return153        }154    }155156    public var stringValue: String? {157        if case let .string(value) = self {158            return value159        } else {160            return nil161        }162    }163164    public var intValue: Int? {165        if case let .int(value) = self {166            return value167        } else {168            return nil169        }170    }171172    public var doubleValue: Double? {173        switch self {174        case let .int(value):175            return Double(value)176        case let .double(value):177            return value178        default:179            return nil180        }181    }182183    public var boolValue: Bool? {184        if case let .bool(value) = self {185            return value186        } else {187            return nil188        }189    }190}191192public struct GeneralTokenExtension: Hashable, Codable {193    public let website: String?194    public let bridgeContract: String?195    public let assetContract: String?196    public let address: String?197    public let explorer: String?198    public let twitter: String?199    public let github: String?200    public let medium: String?201    public let tgann: String?202    public let tggroup: String?203    public let discord: String?204    public let serumV3Usdt: String?205    public let serumV3Usdc: String?206    public let coingeckoId: String?207    public let imageUrl: String?208    public let description: String?209210    public init(data: [String: TokenExtensionValue]) {211        website = data["stringValue"]?.stringValue212        bridgeContract = data["bridgeContract"]?.stringValue213        assetContract = data["assetContract"]?.stringValue214        address = data["address"]?.stringValue215        explorer = data["explorer"]?.stringValue216        twitter = data["twitter"]?.stringValue217        github = data["github"]?.stringValue218        medium = data["medium"]?.stringValue219        tgann = data["tgann"]?.stringValue220        tggroup = data["tggroup"]?.stringValue221        discord = data["discord"]?.stringValue222        serumV3Usdt = data["serumV3Usdt"]?.stringValue223        serumV3Usdc = data["serumV3Usdc"]?.stringValue224        coingeckoId = data["coingeckoId"]?.stringValue225        imageUrl = data["imageUrl"]?.stringValue226        description = data["description"]?.stringValue227    }228229    public init(230        website: String? = nil,231        bridgeContract: String? = nil,232        assetContract: String? = nil,233        address: String? = nil,234        explorer: String? = nil,235        twitter: String? = nil,236        github: String? = nil,237        medium: String? = nil,238        tgann: String? = nil,239        tggroup: String? = nil,240        discord: String? = nil,241        serumV3Usdt: String? = nil,242        serumV3Usdc: String? = nil,243        coingeckoId: String?,244        imageUrl: String? = nil,245        description: String? = nil246    ) {247        self.website = website248        self.bridgeContract = bridgeContract249        self.assetContract = assetContract250        self.address = address251        self.explorer = explorer252        self.twitter = twitter253        self.github = github254        self.medium = medium255        self.tgann = tgann256        self.tggroup = tggroup257        self.discord = discord258        self.serumV3Usdt = serumV3Usdt259        self.serumV3Usdc = serumV3Usdc260        self.coingeckoId = coingeckoId261        self.imageUrl = imageUrl262        self.description = description263    }264}265