diff --git a/Encrypted Ink/Ethereum/AccountsService.swift b/Encrypted Ink/Ethereum/AccountsService.swift index ccc7e32c..c40f8322 100644 --- a/Encrypted Ink/Ethereum/AccountsService.swift +++ b/Encrypted Ink/Ethereum/AccountsService.swift @@ -1,19 +1,62 @@ // Copyright © 2021 Encrypted Ink. All rights reserved. import Foundation +import Web3Swift struct AccountsService { - static func getAccounts() -> [Account] { - return [] + private static let keychainKey = "EncryptedInkStorage" + + static func addAccount(privateKey: String) -> Account? { + guard + let addressBytes = try? EthPrivateKey(hex: privateKey).address().value() + else { + return nil + } + let address = addressBytes.toPrefixedHexString() + let account = Account(privateKey: privateKey, address: address) + var accounts = getAccounts() + guard !accounts.contains(where: { $0.address == address }) else { return nil } + accounts.append(account) + saveInKeychain(accounts: accounts) + return account } - func removeAccount(_ account: Account) { + static func removeAccount(_ account: Account) { + var accounts = getAccounts() + accounts.removeAll(where: {$0.address == account.address }) + saveInKeychain(accounts: accounts) } - func addAccount(privateKey: String) -> Account? { - return nil + static func getAccounts() -> [Account] { + return loadAccountsFromKeychain() ?? [] } + private static func saveInKeychain(accounts: [Account]) { + guard let data = try? JSONEncoder().encode(accounts) else { return } + let query = [kSecClass as String: kSecClassGenericPassword as String, + kSecAttrAccount as String: keychainKey, + kSecValueData as String: data] as [String: Any] + + SecItemDelete(query as CFDictionary) + SecItemAdd(query as CFDictionary, nil) + } + + private static func loadAccountsFromKeychain() -> [Account]? { + guard let returnDataQueryValue = kCFBooleanTrue else { return nil } + let query = [kSecClass as String: kSecClassGenericPassword, + kSecAttrAccount as String: keychainKey, + kSecReturnData as String: returnDataQueryValue, + kSecMatchLimit as String: kSecMatchLimitOne] as [String: Any] + + var dataTypeRef: AnyObject? + let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef) + if status == noErr, let data = dataTypeRef as? Data { + let accounts = try? JSONDecoder().decode([Account].self, from: data) + return accounts + } else { + return nil + } + } } diff --git a/Encrypted Ink/Ethereum/Ethereum.swift b/Encrypted Ink/Ethereum/Ethereum.swift index 2c5dc6f8..9fc495a2 100644 --- a/Encrypted Ink/Ethereum/Ethereum.swift +++ b/Encrypted Ink/Ethereum/Ethereum.swift @@ -13,7 +13,7 @@ struct Transaction { let contractCall: String } -struct Account { +struct Account: Codable { let privateKey: String let address: String }