import Foundation /// Base58 with the XRP Ledger alphabet and Base58Check helpers. enum XRPBase58 { static let alphabet: [Character] = Array("rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz") private static let valueOf: [Character: Int] = { var map: [Character: Int] = [:] for (index, character) in alphabet.enumerated() { map[character] = index } return map }() static func encode(_ bytes: [UInt8]) -> String { var zeros = 0 for byte in bytes { if byte == 0 { zeros += 1 } else { break } } var digits: [Int] = [] for byte in bytes[zeros...] { var carry = Int(byte) for index in digits.indices { carry += digits[index] << 8 digits[index] = carry % 58 carry /= 58 } while carry > 0 { digits.append(carry % 58) carry /= 58 } } var output = String(repeating: String(alphabet[0]), count: zeros) for digit in digits.reversed() { output.append(alphabet[digit]) } return output } static func decode(_ string: String) throws -> [UInt8] { let characters = Array(string) var zeros = 0 for character in characters { if character == alphabet[0] { zeros += 1 } else { break } } var bytes: [Int] = [] for character in characters[zeros...] { guard let value = valueOf[character] else { throw XRPError.invalidBase58 } var carry = value for index in bytes.indices { carry += bytes[index] * 58 bytes[index] = carry & 0xff carry >>= 8 } while carry > 0 { bytes.append(carry & 0xff) carry >>= 8 } } return [UInt8](repeating: 0, count: zeros) + bytes.reversed().map { UInt8($0) } } static func checkEncode(version: [UInt8], payload: [UInt8]) -> String { let body = version + payload return encode(body + XRPHash.checksum(body)) } static func checkDecode(_ string: String, versionLength: Int) throws -> [UInt8] { let decoded = try decode(string) guard decoded.count > versionLength + 4 else { throw XRPError.invalidAddress } let body = Array(decoded[0..<(decoded.count - 4)]) let checksum = Array(decoded[(decoded.count - 4)...]) guard XRPHash.checksum(body) == checksum else { throw XRPError.invalidAddress } return Array(body[versionLength...]) } }