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. 89
      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. 29
      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(
session: session,
account: account,
transaction: unconfirmedTransaction,
gasPrice: .none
transaction: unconfirmedTransaction
)
//addCoordinator(configurator)
@ -63,7 +62,7 @@ extension BrowserCoordinator: BrowserViewControllerDelegate {
switch type {
case .signedTransaction(let 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):
self.delegate?.didSentTransaction(transaction: transaction, in: self)
}

@ -17,16 +17,24 @@ extension DappAction {
let to = Address(string: command.object["to"]?.value ?? "")
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(
transferType: .ether(destination: .none),
value: value,
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)
case .signMessage, .signPersonalMessage:
NSLog("command.object \(command.object)")
return .signMessage("")
let data = command.object["data"]?.value ?? ""
return .signMessage(data)
case .unknown:
return .unknown
}

@ -4,6 +4,7 @@ import Foundation
import UIKit
import WebKit
import JavaScriptCore
import Result
struct DappCommandObjectValue: Decodable {
public var value: String = ""
@ -28,6 +29,10 @@ enum DappCallbackValue {
}
}
enum DAppError: Error {
case cancelled
}
struct DappCallback {
let id: Int
let value: DappCallbackValue
@ -35,6 +40,7 @@ struct DappCallback {
struct DappCommand: Decodable {
let name: Method
let id: Int
let object: [String: DappCommandObjectValue]
}
@ -92,18 +98,18 @@ class BrowserViewController: UIViewController {
"""
let callbacksCount = 0;
let callbacks = {};
var callback_
function addCallback(callbacksCount, cb) {
callbacks[callbacksCount] = cb
function addCallback(cb) {
callbacksCount++
callbacks[callbacksCount] = cb
return callbacksCount
}
function executeCallback(id, value) {
function executeCallback(id, error, value) {
console.log("executeCallback")
let callback = callbacks[id](null, value)
console.log("id", id)
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({
@ -113,21 +119,23 @@ class BrowserViewController: UIViewController {
rpcUrl: "\(session.config.rpcURL.absoluteString)",
sendTransaction: function(tx, cb) {
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) {
console.log("here2.", tx)
addCallback(callbacksCount, cb)
webkit.messageHandlers.signTransaction.postMessage({"name": "signTransaction", "object": tx})
callback_ = cb
let id = addCallback(cb)
webkit.messageHandlers.signTransaction.postMessage({"name": "signTransaction", "object": tx, id: id})
},
signMessage: function(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) {
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()
@ -171,19 +179,24 @@ class BrowserViewController: UIViewController {
// if let url = Bundle.main.url(forResource: "demo", withExtension: "html") {
// 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) {
fatalError("init(coder:) has not been implemented")
}
func notifyFinish(callback: DappCallback) {
let evString = "callback_(null, \"\(callback.value.object)\")"
NSLog("evString \(evString)")
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.webView.evaluateJavaScript(evString, completionHandler: nil)
}
func notifyFinish(callbackID: Int, value: Result<DappCallback, DAppError>) {
let script: String = {
switch value {
case .success(let result):
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) {
let method = Method(string: message.name)
guard let body = message.body as? [String: AnyObject],
let jsonString = body.jsonString,
let command = try? decoder.decode(DappCommand.self, from: jsonString.data(using: .utf8)!) else {
return
}
let action = DappAction.fromCommand(command)
switch method {
case .sendTransaction, .signTransaction:
guard let body = message.body as? [String: AnyObject],
let jsonString = body.jsonString else { return }
let command = try! decoder.decode(DappCommand.self, from: jsonString.data(using: .utf8)!)
let action = DappAction.fromCommand(command)
delegate?.didCall(action: action, callbackID: 0)
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)
//delegate?.didCall(action: action)
} catch {
NSLog("error \(error)")
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 JSONRPCKit
import TrustKeystore
import BigInt
struct EstimateGasRequest: JSONRPCKit.Request {
typealias Response = String
let from: Address
let to: Address?
let value: BigInt
let data: Data
var method: String {
@ -15,7 +18,15 @@ struct EstimateGasRequest: JSONRPCKit.Request {
}
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 {

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

@ -64,7 +64,7 @@ class SendCoordinator: Coordinator {
case .ether(let destination):
controller.addressRow?.value = destination?.description
controller.addressRow?.cell.row.updateCell()
case .token, .exchange: break
case .token: break
}
controller.delegate = self
return controller
@ -76,13 +76,12 @@ class SendCoordinator: Coordinator {
}
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(
session: session,
account: account,
transaction: transaction,
gasPrice: gasPrice
transaction: transaction
)
let controller = ConfirmPaymentViewController(
session: session,

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

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

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

@ -62,6 +62,7 @@ class ConfirmPaymentViewController: UIViewController {
case .failure(let error):
self.displayError(error: error)
}
}
configurator.configurationUpdate.subscribe { [weak self] _ in
guard let `self` = self else { return }
@ -157,7 +158,7 @@ class ConfirmPaymentViewController: UIViewController {
self.displayLoading()
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 }
switch result {
case .success(let type):

@ -13,7 +13,6 @@ protocol SendViewControllerDelegate: class {
func didPressConfirm(
transaction: UnconfirmedTransaction,
transferType: TransferType,
gasPrice: BigInt?,
in viewController: SendViewController
)
}
@ -165,7 +164,7 @@ class SendViewController: FormViewController {
let parsedValue: BigInt? = {
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)
case .token(let token):
return EtherNumberFormatter.full.number(from: amountString, decimals: token.decimals)
@ -180,9 +179,12 @@ class SendViewController: FormViewController {
transferType: transferType,
value: value,
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() {

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

@ -106,50 +106,6 @@ struct ConfirmPaymentDetailsViewModel {
return amountAttributedText(
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):
view.viewModel = BalanceTokenViewModel(token: token)
case .exchange:
break //
}
return view
}

Loading…
Cancel
Save