Support arbitrum, polygon and optimism

This commit is contained in:
Ivan Grachyov 2021-08-06 23:39:58 +03:00
parent ce010da765
commit 9798e03433
8 changed files with 84 additions and 28 deletions

View File

@ -36,6 +36,8 @@
2C901C4A2689F01700D0926A /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C901C492689F01700D0926A /* Strings.swift */; };
2C901C4D268A033100D0926A /* GasService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C901C4C268A033100D0926A /* GasService.swift */; };
2C917429267D2A6E00049075 /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C917428267D2A6E00049075 /* Keychain.swift */; };
2C9F0B6526BDC9AF008FA3D6 /* EthereumNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9F0B6426BDC9AF008FA3D6 /* EthereumNetwork.swift */; };
2C9F0B6826BDCB2E008FA3D6 /* EthereumChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9F0B6726BDCB2E008FA3D6 /* EthereumChain.swift */; };
2CC0CDBE2692027E0072922A /* PriceService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC0CDBD2692027E0072922A /* PriceService.swift */; };
2CC8946F269A2E8C00879245 /* SessionStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC8946E269A2E8C00879245 /* SessionStorage.swift */; };
2CC89471269A334A00879245 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC89470269A334A00879245 /* UserDefaults.swift */; };
@ -80,6 +82,8 @@
2C901C492689F01700D0926A /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
2C901C4C268A033100D0926A /* GasService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GasService.swift; sourceTree = "<group>"; };
2C917428267D2A6E00049075 /* Keychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = "<group>"; };
2C9F0B6426BDC9AF008FA3D6 /* EthereumNetwork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumNetwork.swift; sourceTree = "<group>"; };
2C9F0B6726BDCB2E008FA3D6 /* EthereumChain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumChain.swift; sourceTree = "<group>"; };
2CC0CDBD2692027E0072922A /* PriceService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceService.swift; sourceTree = "<group>"; };
2CC8946E269A2E8C00879245 /* SessionStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionStorage.swift; sourceTree = "<group>"; };
2CC89470269A334A00879245 /* UserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = "<group>"; };
@ -175,6 +179,8 @@
isa = PBXGroup;
children = (
2C1995552674D0F300A8E370 /* Ethereum.swift */,
2C9F0B6426BDC9AF008FA3D6 /* EthereumNetwork.swift */,
2C9F0B6726BDCB2E008FA3D6 /* EthereumChain.swift */,
2CE3D014267F73E80032A62E /* LegacyAccountWithKey.swift */,
2CE3D011267F73C00032A62E /* Transaction.swift */,
);
@ -382,10 +388,12 @@
2CC0CDBE2692027E0072922A /* PriceService.swift in Sources */,
2C8A09C6267513FC00993638 /* Agent.swift in Sources */,
2C8A09D42675184700993638 /* Window.swift in Sources */,
2C9F0B6526BDC9AF008FA3D6 /* EthereumNetwork.swift in Sources */,
2C208A9F26813408005BA500 /* Secrets.swift in Sources */,
2CC8946F269A2E8C00879245 /* SessionStorage.swift in Sources */,
2CD0669126B5537B00728C20 /* InkWallet.swift in Sources */,
2C8A09D726751A0C00993638 /* WalletConnect.swift in Sources */,
2C9F0B6826BDCB2E008FA3D6 /* EthereumChain.swift in Sources */,
2C03D1D2269B407900EF10EA /* NetworkMonitor.swift in Sources */,
2C8A09E326757FC000993638 /* AccountCellView.swift in Sources */,
2C6B964C26B9D92500D2C819 /* NSColor.swift in Sources */,

View File

@ -77,9 +77,9 @@ class Agent: NSObject {
windowController.contentViewController = accountsList
}
func showApprove(transaction: Transaction, peerMeta: WCPeerMeta?, completion: @escaping (Transaction?) -> Void) {
func showApprove(transaction: Transaction, chain: EthereumChain, peerMeta: WCPeerMeta?, completion: @escaping (Transaction?) -> Void) {
let windowController = Window.showNew()
let approveViewController = ApproveTransactionViewController.with(transaction: transaction, peerMeta: peerMeta) { [weak self] transaction in
let approveViewController = ApproveTransactionViewController.with(transaction: transaction, chain: chain, peerMeta: peerMeta) { [weak self] transaction in
if transaction != nil {
self?.askAuthentication(on: windowController.window, onStart: false, reason: Strings.sendTransaction) { success in
completion(success ? transaction : nil)

View File

@ -19,10 +19,7 @@ struct Ethereum {
static let shared = Ethereum()
private let network: Network = AlchemyNetwork(
chain: "mainnet",
apiKey: Secrets.alchemy
)
private let networks = EthereumNetwork.allByChain
func sign(message: String, wallet: InkWallet) throws -> String {
guard let privateKeyString = wallet.ethereumPrivateKeyString else { throw Error.keyNotFound }
@ -58,8 +55,9 @@ struct Ethereum {
return signed
}
func send(transaction: Transaction, wallet: InkWallet) throws -> String {
let bytes = try signedTransactionBytes(transaction: transaction, wallet: wallet)
func send(transaction: Transaction, wallet: InkWallet, chain: EthereumChain) throws -> String {
guard let network = networks[chain] else { throw Error.invalidInputData }
let bytes = try signedTransactionBytes(transaction: transaction, wallet: wallet, chain: chain)
let response = try SendRawTransactionProcedure(network: network, transactionBytes: bytes).call()
guard let hash = response["result"].string else {
throw Error.failedToSendTransaction
@ -67,7 +65,8 @@ struct Ethereum {
return hash
}
private func signedTransactionBytes(transaction: Transaction, wallet: InkWallet) throws -> EthContractCallBytes {
private func signedTransactionBytes(transaction: Transaction, wallet: InkWallet, chain: EthereumChain) throws -> EthContractCallBytes {
guard let network = networks[chain] else { throw Error.invalidInputData }
guard let privateKeyString = wallet.ethereumPrivateKeyString else { throw Error.keyNotFound }
let senderKey = EthPrivateKey(hex: privateKeyString)
let contractAddress = EthAddress(hex: transaction.to)
@ -106,11 +105,11 @@ struct Ethereum {
return bytes
}
func prepareTransaction(_ transaction: Transaction, completion: @escaping (Transaction) -> Void) {
func prepareTransaction(_ transaction: Transaction, chain: EthereumChain, completion: @escaping (Transaction) -> Void) {
var transaction = transaction
if transaction.nonce == nil {
getNonce(from: transaction.from) { nonce in
getNonce(chain: chain, from: transaction.from) { nonce in
transaction.nonce = nonce
completion(transaction)
}
@ -118,7 +117,7 @@ struct Ethereum {
func getGasIfNeeded(gasPrice: String) {
guard transaction.gas == nil else { return }
getGas(from: transaction.from, to: transaction.to, gasPrice: gasPrice, weiAmount: transaction.weiAmount, data: transaction.data) { gas in
getGas(chain: chain, from: transaction.from, to: transaction.to, gasPrice: gasPrice, weiAmount: transaction.weiAmount, data: transaction.data) { gas in
transaction.gas = gas
completion(transaction)
}
@ -127,7 +126,7 @@ struct Ethereum {
if let gasPrice = transaction.gasPrice {
getGasIfNeeded(gasPrice: gasPrice)
} else {
getGasPrice { gasPrice in
getGasPrice(chain: chain) { gasPrice in
transaction.gasPrice = gasPrice
completion(transaction)
if let gasPrice = gasPrice {
@ -138,7 +137,8 @@ struct Ethereum {
}
private func getGas(from: String, to: String, gasPrice: String, weiAmount: EthNumber, data: String, completion: @escaping (String?) -> Void) {
private func getGas(chain: EthereumChain, from: String, to: String, gasPrice: String, weiAmount: EthNumber, data: String, completion: @escaping (String?) -> Void) {
guard let network = networks[chain] else { return }
queue.async {
let gas = try? EthGasEstimate(
network: network,
@ -162,7 +162,8 @@ struct Ethereum {
}
}
private func getGasPrice(completion: @escaping (String?) -> Void) {
private func getGasPrice(chain: EthereumChain, completion: @escaping (String?) -> Void) {
guard let network = networks[chain] else { return }
queue.async {
let gasPrice = try? EthGasPrice(network: network).value().toHexString()
DispatchQueue.main.async {
@ -171,7 +172,8 @@ struct Ethereum {
}
}
private func getNonce(from: String, completion: @escaping (String?) -> Void) {
private func getNonce(chain: EthereumChain, from: String, completion: @escaping (String?) -> Void) {
guard let network = networks[chain] else { return }
queue.async {
let nonce = try? EthTransactions(network: network, address: EthAddress(hex: from), blockChainState: PendingBlockChainState()).count().value().toHexString()
DispatchQueue.main.async {

View File

@ -0,0 +1,15 @@
// Copyright © 2021 Encrypted Ink. All rights reserved.
import Foundation
enum EthereumChain: Int {
case main = 1
case arbitrum = 42161
case polygon = 137
case optimism = 10
var id: Int {
return rawValue
}
}

View File

@ -0,0 +1,29 @@
// Copyright © 2021 Encrypted Ink. All rights reserved.
import Foundation
import Web3Swift
final class EthereumNetwork: Network {
static let allByChain: [EthereumChain: EthereumNetwork] = [
.main: EthereumNetwork(url: "https://eth-mainnet.alchemyapi.io/v2/" + Secrets.alchemy),
.polygon: EthereumNetwork(url: "https://polygon-mainnet.g.alchemy.com/v2/" + Secrets.alchemy),
.arbitrum: EthereumNetwork(url: "https://arb-mainnet.g.alchemy.com/v2/" + Secrets.alchemy),
.optimism: EthereumNetwork(url: "https://mainnet.optimism.io")
]
private let origin: GethNetwork
init(url: String) {
origin = GethNetwork(url: url)
}
func id() throws -> IntegerScalar {
return try origin.id()
}
func call(method: String, params: [EthParameter]) throws -> Data {
return try origin.call(method: method, params: params)
}
}

View File

@ -8,7 +8,7 @@ class AccountsListViewController: NSViewController {
private let walletsManager = WalletsManager.shared
private var cellModels = [CellModel]()
private let chainId = 1
private var chain = EthereumChain.main
var onSelectedWallet: ((Int, InkWallet) -> Void)?
var newWalletId: String?
@ -259,7 +259,7 @@ extension AccountsListViewController: NSTableViewDelegate {
case .wallet:
let wallet = wallets[row]
if let onSelectedWallet = onSelectedWallet {
onSelectedWallet(chainId, wallet)
onSelectedWallet(chain.id, wallet)
} else {
Timer.scheduledTimer(withTimeInterval: 0.01, repeats: false) { [weak self] _ in
var point = NSEvent.mouseLocation

View File

@ -26,12 +26,14 @@ class ApproveTransactionViewController: NSViewController {
private let priceService = PriceService.shared
private var currentGasInfo: GasService.Info?
private var transaction: Transaction!
private var chain: EthereumChain!
private var completion: ((Transaction?) -> Void)!
private var didEnableSpeedConfiguration = false
private var peerMeta: WCPeerMeta?
static func with(transaction: Transaction, peerMeta: WCPeerMeta?, completion: @escaping (Transaction?) -> Void) -> ApproveTransactionViewController {
static func with(transaction: Transaction, chain: EthereumChain, peerMeta: WCPeerMeta?, completion: @escaping (Transaction?) -> Void) -> ApproveTransactionViewController {
let new = instantiate(ApproveTransactionViewController.self)
new.chain = chain
new.transaction = transaction
new.completion = completion
new.peerMeta = peerMeta
@ -58,7 +60,7 @@ class ApproveTransactionViewController: NSViewController {
}
private func prepareTransaction() {
ethereum.prepareTransaction(transaction) { [weak self] updated in
ethereum.prepareTransaction(transaction, chain: chain) { [weak self] updated in
self?.transaction = updated
self?.updateInterface()
}

View File

@ -112,7 +112,7 @@ class WalletConnect {
}
interactor.eth.onTransaction = { [weak self, weak interactor] (id, event, transaction) in
self?.approveTransaction(id: id, wct: transaction, walletId: walletId, interactor: interactor)
self?.approveTransaction(id: id, wct: transaction, walletId: walletId, chainId: chainId, interactor: interactor)
self?.sessionStorage.didInteractWith(clientId: interactor?.clientId)
}
}
@ -128,17 +128,17 @@ class WalletConnect {
}
}
private func approveTransaction(id: Int64, wct: WCEthereumTransaction, walletId: String, interactor: WCInteractor?) {
guard let to = wct.to else {
private func approveTransaction(id: Int64, wct: WCEthereumTransaction, walletId: String, chainId: Int, interactor: WCInteractor?) {
guard let to = wct.to, let chain = EthereumChain(rawValue: chainId) else {
rejectRequest(id: id, interactor: interactor, message: "Something went wrong.")
return
}
let peer = getPeerOfInteractor(interactor)
let transaction = Transaction(from: wct.from, to: to, nonce: wct.nonce, gasPrice: wct.gasPrice, gas: wct.gas, value: wct.value, data: wct.data)
Agent.shared.showApprove(transaction: transaction, peerMeta: peer) { [weak self, weak interactor] transaction in
Agent.shared.showApprove(transaction: transaction, chain: chain, peerMeta: peer) { [weak self, weak interactor] transaction in
if let transaction = transaction {
self?.sendTransaction(transaction, walletId: walletId, requestId: id, interactor: interactor)
self?.sendTransaction(transaction, walletId: walletId, chainId: chainId, requestId: id, interactor: interactor)
} else {
self?.rejectRequest(id: id, interactor: interactor, message: "Cancelled")
}
@ -176,12 +176,12 @@ class WalletConnect {
interactor?.rejectRequest(id: id, message: message).cauterize()
}
private func sendTransaction(_ transaction: Transaction, walletId: String, requestId: Int64, interactor: WCInteractor?) {
guard let wallet = walletsManager.getWallet(id: walletId) else {
private func sendTransaction(_ transaction: Transaction, walletId: String, chainId: Int, requestId: Int64, interactor: WCInteractor?) {
guard let wallet = walletsManager.getWallet(id: walletId), let chain = EthereumChain(rawValue: chainId) else {
rejectRequest(id: requestId, interactor: interactor, message: "Something went wrong.")
return
}
guard let hash = try? ethereum.send(transaction: transaction, wallet: wallet) else {
guard let hash = try? ethereum.send(transaction: transaction, wallet: wallet, chain: chain) else {
rejectRequest(id: requestId, interactor: interactor, message: "Failed to send")
return
}