Adjusting dapps integration

pull/2/head
Michael Scoff 7 years ago
parent e454f0e914
commit 1b7a7e8308
  1. 5
      Trust/Browser/Coordinators/BrowserCoordinator.swift
  2. 12
      Trust/Browser/Types/DappAction.swift
  3. 85
      Trust/Browser/ViewControllers/BrowserViewController.swift
  4. 13
      Trust/EtherClient/Requests/EstimateGasRequest.swift
  5. 64
      Trust/Transfer/Controllers/TransactionConfigurator.swift
  6. 7
      Trust/Transfer/Coordinators/SendCoordinator.swift
  7. 13
      Trust/Transfer/Coordinators/SendTransactionCoordinator.swift
  8. 3
      Trust/Transfer/Types/TransferType.swift
  9. 4
      Trust/Transfer/Types/UnconfirmedTransaction.swift
  10. 3
      Trust/Transfer/ViewControllers/ConfirmPaymentViewController.swift
  11. 10
      Trust/Transfer/ViewControllers/SendViewController.swift
  12. 2
      Trust/Transfer/ViewModels/ConfigureTransactionViewModel.swift
  13. 44
      Trust/Transfer/ViewModels/ConfirmPaymentDetailsViewModel.swift
  14. 2
      Trust/UI/BalanceTitleView.swift

@ -48,8 +48,7 @@ extension BrowserCoordinator: BrowserViewControllerDelegate {
let configurator = TransactionConfigurator( let configurator = TransactionConfigurator(
session: session, session: session,
account: account, account: account,
transaction: unconfirmedTransaction, transaction: unconfirmedTransaction
gasPrice: .none
) )
//addCoordinator(configurator) //addCoordinator(configurator)
@ -63,7 +62,7 @@ extension BrowserCoordinator: BrowserViewControllerDelegate {
switch type { switch type {
case .signedTransaction(let data): case .signedTransaction(let data):
let callback = DappCallback(id: callbackID, value: .signTransaction(data)) let callback = DappCallback(id: callbackID, value: .signTransaction(data))
self.rootViewController.notifyFinish(callback: callback) self.rootViewController.notifyFinish(callbackID: callbackID, value: .success(callback))
case .sentTransaction(let transaction): case .sentTransaction(let transaction):
self.delegate?.didSentTransaction(transaction: transaction, in: self) self.delegate?.didSentTransaction(transaction: transaction, in: self)
} }

@ -17,16 +17,24 @@ extension DappAction {
let to = Address(string: command.object["to"]?.value ?? "") let to = Address(string: command.object["to"]?.value ?? "")
let value = BigInt((command.object["value"]?.value ?? "0").drop0x, radix: 16) ?? BigInt() let value = BigInt((command.object["value"]?.value ?? "0").drop0x, radix: 16) ?? BigInt()
let nonce = BigInt((command.object["nonce"]?.value ?? "0").drop0x, radix: 16) ?? BigInt()
let gasLimit = BigInt((command.object["gas"]?.value ?? "0").drop0x, radix: 16) ?? BigInt()
let gasPrice = BigInt((command.object["gasPrice"]?.value ?? "0").drop0x, radix: 16) ?? BigInt()
let unconfirmedTransaction = UnconfirmedTransaction( let unconfirmedTransaction = UnconfirmedTransaction(
transferType: .ether(destination: .none), transferType: .ether(destination: .none),
value: value, value: value,
to: to, to: to,
data: Data(hex: command.object["data"]!.value) data: Data(hex: command.object["data"]!.value),
gasLimit: gasLimit,
gasPrice: gasPrice,
nonce: nonce
) )
return .signTransaction(unconfirmedTransaction) return .signTransaction(unconfirmedTransaction)
case .signMessage, .signPersonalMessage: case .signMessage, .signPersonalMessage:
NSLog("command.object \(command.object)") NSLog("command.object \(command.object)")
return .signMessage("") let data = command.object["data"]?.value ?? ""
return .signMessage(data)
case .unknown: case .unknown:
return .unknown return .unknown
} }

@ -4,6 +4,7 @@ import Foundation
import UIKit import UIKit
import WebKit import WebKit
import JavaScriptCore import JavaScriptCore
import Result
struct DappCommandObjectValue: Decodable { struct DappCommandObjectValue: Decodable {
public var value: String = "" public var value: String = ""
@ -28,6 +29,10 @@ enum DappCallbackValue {
} }
} }
enum DAppError: Error {
case cancelled
}
struct DappCallback { struct DappCallback {
let id: Int let id: Int
let value: DappCallbackValue let value: DappCallbackValue
@ -35,6 +40,7 @@ struct DappCallback {
struct DappCommand: Decodable { struct DappCommand: Decodable {
let name: Method let name: Method
let id: Int
let object: [String: DappCommandObjectValue] let object: [String: DappCommandObjectValue]
} }
@ -92,18 +98,18 @@ class BrowserViewController: UIViewController {
""" """
let callbacksCount = 0; let callbacksCount = 0;
let callbacks = {}; let callbacks = {};
var callback_ function addCallback(cb) {
function addCallback(callbacksCount, cb) {
callbacks[callbacksCount] = cb
callbacksCount++ callbacksCount++
callbacks[callbacksCount] = cb
return callbacksCount
} }
function executeCallback(id, value) { function executeCallback(id, error, value) {
console.log("executeCallback") console.log("executeCallback")
let callback = callbacks[id](null, value)
console.log("id", id) console.log("id", id)
console.log("value", value) console.log("value", value)
//invalid argument 0: json: cannot unmarshal non-string into Go value of type hexutil.Byte console.log("error", error)
let callback = callbacks[id](error, value)
} }
const engine = ZeroClientProvider({ const engine = ZeroClientProvider({
@ -113,21 +119,23 @@ class BrowserViewController: UIViewController {
rpcUrl: "\(session.config.rpcURL.absoluteString)", rpcUrl: "\(session.config.rpcURL.absoluteString)",
sendTransaction: function(tx, cb) { sendTransaction: function(tx, cb) {
console.log("here." + tx) console.log("here." + tx)
webkit.messageHandlers.postMessage({"name": "sendTransaction", "object": tx}) let id = addCallback(cb)
webkit.messageHandlers.postMessage({"name": "sendTransaction", "object": tx, id: id})
}, },
signTransaction: function(tx, cb) { signTransaction: function(tx, cb) {
console.log("here2.", tx) console.log("here2.", tx)
addCallback(callbacksCount, cb) let id = addCallback(cb)
webkit.messageHandlers.signTransaction.postMessage({"name": "signTransaction", "object": tx}) webkit.messageHandlers.signTransaction.postMessage({"name": "signTransaction", "object": tx, id: id})
callback_ = cb
}, },
signMessage: function(cb) { signMessage: function(cb) {
console.log("here.4", cb) console.log("here.4", cb)
webkit.messageHandlers.signMessage.postMessage({"name": "signMessage", "object": message}) let id = addCallback(cb)
webkit.messageHandlers.signMessage.postMessage({"name": "signMessage", "object": message, id: id})
}, },
signPersonalMessage: function(message, cb) { signPersonalMessage: function(message, cb) {
console.log("here.5", cb) console.log("here.5", cb)
webkit.messageHandlers.signPersonalMessage.postMessage({"name": "signPersonalMessage", "object": message}) let id = addCallback(cb)
webkit.messageHandlers.signPersonalMessage.postMessage({"name": "signPersonalMessage", "object": message, id: id})
}, },
}) })
engine.start() engine.start()
@ -171,19 +179,24 @@ class BrowserViewController: UIViewController {
// if let url = Bundle.main.url(forResource: "demo", withExtension: "html") { // if let url = Bundle.main.url(forResource: "demo", withExtension: "html") {
// webView.load(URLRequest(url: url)) // webView.load(URLRequest(url: url))
// } // }
webView.load(URLRequest(url: URL(string: "https://tokenfactory.netlify.com/#/factory")!)) webView.load(URLRequest(url: URL(string: "https://ropsten.kyber.network/")!))
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
func notifyFinish(callback: DappCallback) { func notifyFinish(callbackID: Int, value: Result<DappCallback, DAppError>) {
let evString = "callback_(null, \"\(callback.value.object)\")" let script: String = {
NSLog("evString \(evString)") switch value {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { case .success(let result):
self.webView.evaluateJavaScript(evString, completionHandler: nil) return "executeCallback(\(callbackID), null, \"\(result.value.object)\")"
case .failure(let error):
return "executeCallback(\(callbackID), \"\(error)\", null)"
} }
}()
NSLog("script \(script)")
self.webView.evaluateJavaScript(script, completionHandler: nil)
} }
} }
@ -197,36 +210,20 @@ extension BrowserViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
let method = Method(string: message.name) let method = Method(string: message.name)
switch method {
case .sendTransaction, .signTransaction:
guard let body = message.body as? [String: AnyObject], guard let body = message.body as? [String: AnyObject],
let jsonString = body.jsonString else { return } let jsonString = body.jsonString,
let command = try? decoder.decode(DappCommand.self, from: jsonString.data(using: .utf8)!) else {
let command = try! decoder.decode(DappCommand.self, from: jsonString.data(using: .utf8)!)
let action = DappAction.fromCommand(command)
delegate?.didCall(action: action, callbackID: 0)
return return
case .signPersonalMessage: break
//delegate?.didCall(action: .signMessage("hello"))
default: break
} }
guard
let body = message.body as? [String: AnyObject],
let jsonString = body.jsonString
else { return }
do {
let command = try decoder.decode(
DappCommand.self,
from: jsonString.data(using: .utf8)!
)
let action = DappAction.fromCommand(command) let action = DappAction.fromCommand(command)
//delegate?.didCall(action: action)
} catch { switch method {
NSLog("error \(error)") case .sendTransaction, .signTransaction:
delegate?.didCall(action: action, callbackID: command.id)
case .signPersonalMessage:
delegate?.didCall(action: action, callbackID: command.id)
case .signMessage, .unknown:
break
} }
} }
} }

@ -3,11 +3,14 @@
import Foundation import Foundation
import JSONRPCKit import JSONRPCKit
import TrustKeystore import TrustKeystore
import BigInt
struct EstimateGasRequest: JSONRPCKit.Request { struct EstimateGasRequest: JSONRPCKit.Request {
typealias Response = String typealias Response = String
let from: Address
let to: Address? let to: Address?
let value: BigInt
let data: Data let data: Data
var method: String { var method: String {
@ -15,7 +18,15 @@ struct EstimateGasRequest: JSONRPCKit.Request {
} }
var parameters: Any? { var parameters: Any? {
return [["to": to, "data": data.hexEncoded]] let results = [
[
"from": from.description,
"to": to?.description ?? "",
"value": value.description.hexEncoded,
"data": data.hexEncoded,
],
]
return results
} }
func response(from resultObject: Any) throws -> Response { func response(from resultObject: Any) throws -> Response {

@ -24,7 +24,6 @@ class TransactionConfigurator {
let session: WalletSession let session: WalletSession
let account: Account let account: Account
let transaction: UnconfirmedTransaction let transaction: UnconfirmedTransaction
private let gasPrice: BigInt?
var configuration: TransactionConfiguration { var configuration: TransactionConfiguration {
didSet { didSet {
configurationUpdate.value = configuration configurationUpdate.value = configuration
@ -32,32 +31,42 @@ class TransactionConfigurator {
} }
lazy var calculatedGasPrice: BigInt = { lazy var calculatedGasPrice: BigInt = {
return self.gasPrice ?? configuration.gasPrice return transaction.gasPrice ?? configuration.gasPrice
}() }()
var calculatedGasLimit: BigInt? {
return transaction.gasLimit
}
var requestEstimateGas: Bool {
return transaction.gasLimit == .none
}
var configurationUpdate: Subscribable<TransactionConfiguration> = Subscribable(nil) var configurationUpdate: Subscribable<TransactionConfiguration> = Subscribable(nil)
init( init(
session: WalletSession, session: WalletSession,
account: Account, account: Account,
transaction: UnconfirmedTransaction, transaction: UnconfirmedTransaction
gasPrice: BigInt?
) { ) {
self.session = session self.session = session
self.account = account self.account = account
self.transaction = transaction self.transaction = transaction
self.gasPrice = gasPrice
self.configuration = TransactionConfiguration( self.configuration = TransactionConfiguration(
gasPrice: min(max(gasPrice ?? GasPriceConfiguration.default, GasPriceConfiguration.min), GasPriceConfiguration.max), gasPrice: min(max(transaction.gasPrice ?? GasPriceConfiguration.default, GasPriceConfiguration.min), GasPriceConfiguration.max),
gasLimit: GasLimitConfiguration.default, gasLimit: transaction.gasLimit ?? GasLimitConfiguration.default,
data: Data() data: transaction.data ?? Data()
) )
} }
func load(completion: @escaping (Result<Void, AnyError>) -> Void) { func load(completion: @escaping (Result<Void, AnyError>) -> Void) {
switch transaction.transferType { switch transaction.transferType {
case .ether: case .ether:
guard requestEstimateGas else {
return completion(.success(()))
}
estimateGasLimit()
self.configuration = TransactionConfiguration( self.configuration = TransactionConfiguration(
gasPrice: calculatedGasPrice, gasPrice: calculatedGasPrice,
gasLimit: GasLimitConfiguration.default, gasLimit: GasLimitConfiguration.default,
@ -79,31 +88,34 @@ class TransactionConfigurator {
completion(.failure(error)) completion(.failure(error))
} }
} }
case .exchange:
// TODO
self.configuration = TransactionConfiguration(
gasPrice: calculatedGasPrice,
gasLimit: 300_000,
data: Data()
)
completion(.success(()))
} }
estimateGasLimit()
} }
func estimateGasLimit() { func estimateGasLimit() {
let request = EstimateGasRequest(to: transaction.to, data: self.configuration.data) let to: Address? = {
switch transaction.transferType {
case .ether: return transaction.to
case .token(let token):
return Address(string: token.contract)
}
}()
let request = EstimateGasRequest(
from: session.account.address,
to: to,
value: transaction.value,
data: configuration.data
)
Session.send(EtherServiceRequest(batch: BatchFactory().create(request))) { [weak self] result in Session.send(EtherServiceRequest(batch: BatchFactory().create(request))) { [weak self] result in
guard let `self` = self else { return } guard let `self` = self else { return }
switch result { switch result {
case .success(let block): case .success(let gasLimit):
let gasLimit: BigInt = { let gasLimit: BigInt = {
let limit = BigInt(block.drop0x, radix: 16) ?? BigInt() let limit = BigInt(gasLimit.drop0x, radix: 16) ?? BigInt()
if limit == BigInt(21000) { if limit == BigInt(21000) {
return limit return limit
} }
return limit + (limit * 10 / 100) return limit + (limit * 20 / 100)
}() }()
self.configuration = TransactionConfiguration( self.configuration = TransactionConfiguration(
@ -122,7 +134,7 @@ class TransactionConfigurator {
account: account, account: account,
address: transaction.to, address: transaction.to,
contract: .none, contract: .none,
nonce: 0, nonce: -1,
data: configuration.data, data: configuration.data,
gasPrice: configuration.gasPrice, gasPrice: configuration.gasPrice,
gasLimit: configuration.gasLimit, gasLimit: configuration.gasLimit,
@ -134,12 +146,12 @@ class TransactionConfigurator {
let value: BigInt = { let value: BigInt = {
switch transaction.transferType { switch transaction.transferType {
case .ether: return transaction.value case .ether: return transaction.value
case .token, .exchange: return 0 case .token: return 0
} }
}() }()
let address: Address? = { let address: Address? = {
switch transaction.transferType { switch transaction.transferType {
case .ether, .exchange: return transaction.to case .ether: return transaction.to
case .token(let token): return token.address case .token(let token): return token.address
} }
}() }()
@ -147,7 +159,7 @@ class TransactionConfigurator {
value: value, value: value,
account: account, account: account,
to: address, to: address,
nonce: 0, nonce: -1,
data: configuration.data, data: configuration.data,
gasPrice: configuration.gasPrice, gasPrice: configuration.gasPrice,
gasLimit: configuration.gasLimit, gasLimit: configuration.gasLimit,

@ -64,7 +64,7 @@ class SendCoordinator: Coordinator {
case .ether(let destination): case .ether(let destination):
controller.addressRow?.value = destination?.description controller.addressRow?.value = destination?.description
controller.addressRow?.cell.row.updateCell() controller.addressRow?.cell.row.updateCell()
case .token, .exchange: break case .token: break
} }
controller.delegate = self controller.delegate = self
return controller return controller
@ -76,13 +76,12 @@ class SendCoordinator: Coordinator {
} }
extension SendCoordinator: SendViewControllerDelegate { extension SendCoordinator: SendViewControllerDelegate {
func didPressConfirm(transaction: UnconfirmedTransaction, transferType: TransferType, gasPrice: BigInt?, in viewController: SendViewController) { func didPressConfirm(transaction: UnconfirmedTransaction, transferType: TransferType, in viewController: SendViewController) {
let configurator = TransactionConfigurator( let configurator = TransactionConfigurator(
session: session, session: session,
account: account, account: account,
transaction: transaction, transaction: transaction
gasPrice: gasPrice
) )
let controller = ConfirmPaymentViewController( let controller = ConfirmPaymentViewController(
session: session, session: session,

@ -25,25 +25,24 @@ class SendTransactionCoordinator {
} }
func send( func send(
transactions: [SignTransaction], transaction: SignTransaction,
completion: @escaping (Result<ConfirmResult, AnyError>) -> Void completion: @escaping (Result<ConfirmResult, AnyError>) -> Void
) { ) {
if transaction.nonce >= 0 {
signAndSend(transaction: transaction, completion: completion)
} else {
let request = EtherServiceRequest(batch: BatchFactory().create(GetTransactionCountRequest(address: session.account.address.description))) let request = EtherServiceRequest(batch: BatchFactory().create(GetTransactionCountRequest(address: session.account.address.description)))
Session.send(request) { [weak self] result in Session.send(request) { [weak self] result in
guard let `self` = self else { return } guard let `self` = self else { return }
switch result { switch result {
case .success(let count): case .success(let count):
let transactions = self.mergeNonce(transactions: transactions, currentNonce: count) let transaction = self.appendNonce(to: transaction, currentNonce: count)
guard let first = transactions.first else { return } self.signAndSend(transaction: transaction, completion: completion)
self.signAndSend(transaction: first, completion: completion)
case .failure(let error): case .failure(let error):
completion(.failure(AnyError(error))) completion(.failure(AnyError(error)))
} }
} }
} }
private func mergeNonce(transactions: [SignTransaction], currentNonce: Int) -> [SignTransaction] {
return transactions.map { appendNonce(to: $0, currentNonce: currentNonce) }
} }
private func appendNonce(to: SignTransaction, currentNonce: Int) -> SignTransaction { private func appendNonce(to: SignTransaction, currentNonce: Int) -> SignTransaction {

@ -6,7 +6,6 @@ import TrustKeystore
enum TransferType { enum TransferType {
case ether(destination: Address?) case ether(destination: Address?)
case token(TokenObject) case token(TokenObject)
case exchange(from: SubmitExchangeToken, to: SubmitExchangeToken)
} }
extension TransferType { extension TransferType {
@ -16,7 +15,6 @@ extension TransferType {
return server.symbol return server.symbol
case .token(let token): case .token(let token):
return token.symbol return token.symbol
case .exchange: return "--"
} }
} }
@ -26,7 +24,6 @@ extension TransferType {
return TokensDataStore.etherToken(for: Config()).contract return TokensDataStore.etherToken(for: Config()).contract
case .token(let token): case .token(let token):
return token.contract return token.contract
case .exchange: return "--"
} }
} }
} }

@ -9,4 +9,8 @@ struct UnconfirmedTransaction {
let value: BigInt let value: BigInt
let to: Address? let to: Address?
let data: Data? let data: Data?
let gasLimit: BigInt?
let gasPrice: BigInt?
let nonce: BigInt?
} }

@ -62,6 +62,7 @@ class ConfirmPaymentViewController: UIViewController {
case .failure(let error): case .failure(let error):
self.displayError(error: error) self.displayError(error: error)
} }
} }
configurator.configurationUpdate.subscribe { [weak self] _ in configurator.configurationUpdate.subscribe { [weak self] _ in
guard let `self` = self else { return } guard let `self` = self else { return }
@ -157,7 +158,7 @@ class ConfirmPaymentViewController: UIViewController {
self.displayLoading() self.displayLoading()
let transaction = configurator.signTransaction() let transaction = configurator.signTransaction()
self.sendTransactionCoordinator.send(transactions: [transaction]) { [weak self] result in self.sendTransactionCoordinator.send(transaction: transaction) { [weak self] result in
guard let `self` = self else { return } guard let `self` = self else { return }
switch result { switch result {
case .success(let type): case .success(let type):

@ -13,7 +13,6 @@ protocol SendViewControllerDelegate: class {
func didPressConfirm( func didPressConfirm(
transaction: UnconfirmedTransaction, transaction: UnconfirmedTransaction,
transferType: TransferType, transferType: TransferType,
gasPrice: BigInt?,
in viewController: SendViewController in viewController: SendViewController
) )
} }
@ -165,7 +164,7 @@ class SendViewController: FormViewController {
let parsedValue: BigInt? = { let parsedValue: BigInt? = {
switch transferType { switch transferType {
case .ether, .exchange: // exchange doesn't really matter here case .ether: // exchange doesn't really matter here
return EtherNumberFormatter.full.number(from: amountString, units: .ether) return EtherNumberFormatter.full.number(from: amountString, units: .ether)
case .token(let token): case .token(let token):
return EtherNumberFormatter.full.number(from: amountString, decimals: token.decimals) return EtherNumberFormatter.full.number(from: amountString, decimals: token.decimals)
@ -180,9 +179,12 @@ class SendViewController: FormViewController {
transferType: transferType, transferType: transferType,
value: value, value: value,
to: address, to: address,
data: data data: data,
gasLimit: .none,
gasPrice: gasPrice,
nonce: .none
) )
self.delegate?.didPressConfirm(transaction: transaction, transferType: transferType, gasPrice: gasPrice, in: self) self.delegate?.didPressConfirm(transaction: transaction, transferType: transferType, in: self)
} }
@objc func openReader() { @objc func openReader() {

@ -44,7 +44,7 @@ struct ConfigureTransactionViewModel {
var isDataInputHidden: Bool { var isDataInputHidden: Bool {
switch transferType { switch transferType {
case .ether: return false case .ether: return false
case .token, .exchange: return true case .token: return true
} }
} }
} }

@ -106,50 +106,6 @@ struct ConfirmPaymentDetailsViewModel {
return amountAttributedText( return amountAttributedText(
string: fullFormatter.string(from: transaction.value) string: fullFormatter.string(from: transaction.value)
) )
case .exchange(let from, let to):
let fromAttributedString: NSAttributedString = {
let amount = NSAttributedString(
string: fullFormatter.string(from: from.amount),
attributes: [
.font: UIFont.systemFont(ofSize: 24),
.foregroundColor: Colors.red,
]
)
let currency = NSAttributedString(
string: " " + from.token.symbol,
attributes: [
.font: UIFont.systemFont(ofSize: 16),
]
)
return amount + currency
}()
let toAttributedString: NSAttributedString = {
let amount = NSAttributedString(
string: fullFormatter.string(from: to.amount),
attributes: [
.font: UIFont.systemFont(ofSize: 24),
.foregroundColor: Colors.green,
]
)
let currency = NSAttributedString(
string: " " + to.token.symbol,
attributes: [
.font: UIFont.systemFont(ofSize: 16),
]
)
return amount + currency
}()
let amount = NSAttributedString(
string: String(" for "),
attributes: [
.font: UIFont.systemFont(ofSize: 14, weight: UIFont.Weight.light),
]
)
return fromAttributedString + amount + toAttributedString
} }
} }

@ -99,8 +99,6 @@ extension BalanceTitleView {
} }
case .token(let token): case .token(let token):
view.viewModel = BalanceTokenViewModel(token: token) view.viewModel = BalanceTokenViewModel(token: token)
case .exchange:
break //
} }
return view return view
} }

Loading…
Cancel
Save