An advanced Ethereum/EVM mobile wallet
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
alpha-wallet-ios/Trust/Transfer/Controllers/TransactionConfigurator.swift

214 lines
7.6 KiB

// Copyright SIX DAY LLC. All rights reserved.
import Foundation
import BigInt
import Result
import TrustKeystore
import JSONRPCKit
import APIKit
public struct PreviewTransaction {
let value: BigInt
let account: Account
let address: Address?
let contract: Address?
let nonce: Int
let data: Data
let gasPrice: BigInt
let gasLimit: BigInt
let transferType: TransferType
}
class TransactionConfigurator {
let session: WalletSession
let account: Account
let transaction: UnconfirmedTransaction
var configuration: TransactionConfiguration {
didSet {
configurationUpdate.value = configuration
}
}
lazy var calculatedGasPrice: BigInt = {
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
) {
self.session = session
self.account = account
self.transaction = transaction
self.configuration = TransactionConfiguration(
gasPrice: min(max(transaction.gasPrice ?? GasPriceConfiguration.default, GasPriceConfiguration.min), GasPriceConfiguration.max),
gasLimit: transaction.gasLimit ?? GasLimitConfiguration.default,
data: transaction.data ?? Data()
)
}
func estimateGasLimit() {
let to: Address? = {
switch transaction.transferType {
case .ether: return transaction.to
case .token(let token):
return Address(string: token.contract)
case .stormBird(let token):
return Address(string: token.contract)
case .stormBirdOrder(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 gasLimit):
let gasLimit: BigInt = {
let limit = BigInt(gasLimit.drop0x, radix: 16) ?? BigInt()
if limit == BigInt(21000) {
return limit
}
return limit + (limit * 20 / 100)
}()
self.configuration = TransactionConfiguration(
gasPrice: self.calculatedGasPrice,
gasLimit: gasLimit,
data: self.configuration.data
)
case .failure: break
}
}
}
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,
data: transaction.data ?? self.configuration.data
)
completion(.success(()))
case .token:
session.web3.request(request: ContractERC20Transfer(amount: transaction.value, address: transaction.to!.description)) { [unowned self] result in
switch result {
case .success(let res):
let data = Data(hex: res.drop0x)
self.configuration = TransactionConfiguration(
gasPrice: self.calculatedGasPrice,
gasLimit: 144000,
data: data
)
completion(.success(()))
case .failure(let error):
completion(.failure(error))
}
}
//TODO clean up
case .stormBird:
session.web3.request(request: ContractStormBirdTransfer(address: transaction.to!.description, indices: (transaction.indices)!)) { [unowned self] result in
switch result {
case .success(let res):
let data = Data(hex: res.drop0x)
self.configuration = TransactionConfiguration(
gasPrice: self.calculatedGasPrice,
gasLimit: 144000,
data: data
)
completion(.success(()))
case .failure(let error):
completion(.failure(error))
}
}
//TODO put order claim tx here somehow, or maybe the same one above
case .stormBirdOrder:
session.web3.request(request: ClaimStormBirdOrder(expiry: transaction.expiry!, indices: transaction.indices!,
v: transaction.v!, r: transaction.r!, s: transaction.s!)) { [unowned self] result in
switch result {
case .success(let res):
let data = Data(hex: res.drop0x)
self.configuration = TransactionConfiguration(
gasPrice: self.calculatedGasPrice,
gasLimit: 144000,
data: data
)
completion(.success(()))
case .failure(let error):
completion(.failure(error))
}
}
}
}
func previewTransaction() -> PreviewTransaction {
return PreviewTransaction(
value: transaction.value,
account: account,
address: transaction.to,
contract: .none,
nonce: -1,
data: configuration.data,
gasPrice: configuration.gasPrice,
gasLimit: configuration.gasLimit,
transferType: transaction.transferType
)
}
func signTransaction() -> SignTransaction {
let value: BigInt = {
switch transaction.transferType {
case .ether: return transaction.value
case .token: return 0
case .stormBird: return 0
case .stormBirdOrder: return transaction.value
}
}()
let address: Address? = {
switch transaction.transferType {
case .ether: return transaction.to
case .token(let token): return token.address
case .stormBird(let token): return token.address
case .stormBirdOrder(let token): return token.address
}
}()
let signTransaction = SignTransaction(
value: value,
account: account,
to: address,
nonce: -1,
data: configuration.data,
gasPrice: configuration.gasPrice,
gasLimit: configuration.gasLimit,
chainID: session.config.chainID
)
return signTransaction
}
func update(configuration: TransactionConfiguration) {
self.configuration = configuration
}
}