Issue #871

Use libraries

import web3
import web3keystore
import KeychainAccess

private final class KeyStorage: EthereumKeyStorageProtocol {
    enum Key: String {
        case privateKey
        case phrase
    }
    private let keychain = Keychain(service: "com.example.myapp")

    func storePrivateKey(key: Data) throws {
        try keychain.set(key, key: Key.privateKey.rawValue)
    }

    func loadPrivateKey() throws -> Data {
        if let data = try keychain.getData(Key.privateKey.rawValue) {
            return data
        } else {
            throw EthereumKeyStorageError.failedToLoad
        }
    }

    var phrase: String? {
        get {
            try? keychain.getString(Key.phrase.rawValue)
        }
        set {
            if let newValue = newValue {
                try? keychain.set(newValue, key: Key.phrase.rawValue)
            }
        }
    }
}

private struct PolygonSeedPhraser {
    func generate() throws -> String {
        guard
            let seed = SECP256K1.generatePrivateKey(),
            let mnemonics = BIP39.generateMnemonicsFromEntropy(
                entropy: seed,
                language: .english
            )
        else {
            throw PolygonError.invalidMnemonics
        }

        return mnemonics
    }
}

final class PolygonClient {
    static let shared = PolygonClient()

    let client: EthereumClient
    private let storage = KeyStorage()
    private let keystorePassword: String = "my_keystore_password"

    private init() {
        let url = URL(string: "https://rpc-mumbai.matic.today/")!
        client = EthereumClient(url: .url)
    }

    func getAccount() async throws -> EthereumAccount {
        try EthereumAccount(
            keyStorage: storage,
            keystorePassword: keystorePassword
        )
    }

    func createAccount() async throws -> PolygonAccount {
        let phrase = try PolygonSeedPhraser().generate()
        return try await restoreAccount(phrase: phrase)
    }

    func restoreAccount(
        phrase: String
    ) async throws -> PolygonAccount {
        guard let seed = BIP39.mnemonicsToEntropy(phrase, language: .english) else {
            throw PolygonError.invalidMnemonics
        }

        return try EthereumAccount.importAccount(
            keyStorage: storage,
            privateKey: seed.toHexString(),
            keystorePassword: keystorePassword
        )
    }
}

Read more

From https://ethereum.stackexchange.com/questions/3542/how-are-ethereum-addresses-generated

There are three main steps to get from private -> address: Create a random private key (64 (hex) characters / 256 bits / 32 bytes) Derive the public key from this private key (128 (hex) characters / 512 bits / 64 bytes) Derive the address from this public key. (40 (hex) characters / 160 bits / 20 bytes)

From https://ethereum.stackexchange.com/questions/84431/what-are-the-odds-of-getting-same-address-or-private-key-or-12-24-set-of-mnemoni

Mnemonic phrases are generated from 128 bits (12 words) to 256 bits (24 words) of entropy. The probability to guess a mnemonic phrase is 2^-128 to 2^-256, which is very small.