Allow user to set nonce for transaction #1928

pull/1939/head
Hwee-Boon Yar 4 years ago committed by James Sangalli
parent c39e116bf2
commit 205532eb0f
  1. 4
      AlphaWallet/Localization/en.lproj/Localizable.strings
  2. 4
      AlphaWallet/Localization/es.lproj/Localizable.strings
  3. 4
      AlphaWallet/Localization/ja.lproj/Localizable.strings
  4. 4
      AlphaWallet/Localization/ko.lproj/Localizable.strings
  5. 4
      AlphaWallet/Localization/zh-Hans.lproj/Localizable.strings
  6. 3
      AlphaWallet/Transactions/ViewControllers/TransactionViewController.swift
  7. 12
      AlphaWallet/Transactions/ViewModels/TransactionDetailsViewModel.swift
  8. 17
      AlphaWallet/Transfer/Controllers/TransactionConfigurator.swift
  9. 3
      AlphaWallet/Transfer/Types/ConfigureTransactionError.swift
  10. 8
      AlphaWallet/Transfer/Types/TransactionConfiguration.swift
  11. 28
      AlphaWallet/Transfer/ViewControllers/ConfigureTransactionViewController.swift
  12. 8
      AlphaWallet/Transfer/ViewControllers/ConfirmPaymentViewController.swift
  13. 12
      AlphaWallet/Transfer/ViewModels/ConfirmPaymentDetailsViewModel.swift

@ -16,6 +16,7 @@
"Google" = "Google";
"configureTransaction.error.gasFeeTooHigh" = "Gas Fee too high. Max available: %@";
"configureTransaction.error.gasLimitTooHigh" = "Gas Limit too high. Max available: %d";
"configureTransaction.error.nonceNotPositiveNumber" = "Nonce must be a positive number";
"configureTransaction.gasLimit.label.description" = "The gas limit prevents smart contracts from consuming all your %@. We will try to calculate the gas limit automatically for you, but some smart contracts may require a custom gas limit.";
"configureTransaction.gasLimit.label.title" = "Gas Limit";
"configureTransaction.gasPrice.label.description" = "The higher the gas price, the more expensive your transaction fee will be, but the quicker your tranasction will be processed by the %@ network.";
@ -27,6 +28,7 @@
"transaction.time.label.title" = "Transaction time";
"transaction.id.label.title" = "Transaction #";
"transaction.blockNumber.label.title" = "Block #";
"transaction.nonce.label.title" = "Nonce";
"confirmPayment.confirm.button.title" = "Confirm";
"confirmPayment.from.label.title" = "From";
"confirmPayment.gasFee.label.title" = "Network Fee";
@ -127,7 +129,9 @@
"transaction.navigation.title" = "Transaction";
"import.navigation.title" = "Import Wallet";
"configureTransaction.data.label.title" = "Transaction Data (Optional)";
"configureTransaction.nonce.label.title" = "Nonce (Optional)";
"confirmPayment.data.label.title" = "Data";
"confirmPayment.nonce.label.title" = "Nonce";
"Contract Address" = "Contract Address";
"Copy Address" = "Copy Address";
"Decimals" = "Decimals";

@ -16,6 +16,7 @@
"Google" = "Google";
"configureTransaction.error.gasFeeTooHigh" = "Tarifa de gas demasiado alta. Máximo disponible: %@";
"configureTransaction.error.gasLimitTooHigh" = "Límite de gas demasiado alto. Máximo disponible: %d";
"configureTransaction.error.nonceNotPositiveNumber" = "Nonce must be a positive number";
"configureTransaction.gasLimit.label.description" = "El límite de gas evita que los contratos inteligentes consuman todo tu %@. Intentaremos calcular el límite de gas de forma automática, pero algunos contratos inteligentes requieren un límite de gas personalizado.";
"configureTransaction.gasLimit.label.title" = "Límite de gas";
"configureTransaction.gasPrice.label.description" = "Cuanto más alto sea el precio del gas, más cara será la tarifa de tu transacción, pero más rápido se procesará por la red %@.";
@ -27,6 +28,7 @@
"transaction.time.label.title" = "Hora de la transacción";
"transaction.id.label.title" = "Transacción n.º";
"transaction.blockNumber.label.title" = "Bloque n.º";
"transaction.nonce.label.title" = "Nonce";
"confirmPayment.confirm.button.title" = "Confirmar";
"confirmPayment.from.label.title" = "De";
"confirmPayment.gasFee.label.title" = "Tarifa de red";
@ -126,7 +128,9 @@
"transaction.navigation.title" = "Transacción";
"import.navigation.title" = "Importar monedero";
"configureTransaction.data.label.title" = "Datos de la transacción (opcional)";
"configureTransaction.nonce.label.title" = "Nonce (Optional)";
"confirmPayment.data.label.title" = "Datos";
"confirmPayment.nonce.label.title" = "Nonce";
"Contract Address" = "Dirección del contrato";
"Copy Address" = "Copiar dirección";
"Decimals" = "Decimales";

@ -16,6 +16,7 @@
"Google" = "Google";
"configureTransaction.error.gasFeeTooHigh" = "ガス料金が高すぎます。利用可能な最大: %@";
"configureTransaction.error.gasLimitTooHigh" = "ガス制限が高すぎます。利用可能な最大: %d";
"configureTransaction.error.nonceNotPositiveNumber" = "Nonce must be a positive number";
"configureTransaction.gasLimit.label.description" = "ガスの制限によって、スマート契約はすべての %@を消費できなくします。このアプリは、自動的にガスの制限を計算を試行しまが、スマート契約によってはカスタムのガス制限を必要とすることがあります。";
"configureTransaction.gasLimit.label.title" = "ガスの制限";
"configureTransaction.gasPrice.label.description" = "ガスの価格が高くなるほど、トランザクションの手数料が高くなりますが、%@ ネットワークによりトランザクションがより速く処理されます。";
@ -27,6 +28,7 @@
"transaction.time.label.title" = "トランザクション時間";
"transaction.id.label.title" = "トランザクション番号";
"transaction.blockNumber.label.title" = "ブロック番号";
"transaction.nonce.label.title" = "Nonce";
"confirmPayment.confirm.button.title" = "確認";
"confirmPayment.from.label.title" = "送信元";
"confirmPayment.gasFee.label.title" = "ネットワーク料金";
@ -127,7 +129,9 @@
"transaction.navigation.title" = "トランザクション";
"import.navigation.title" = "ウォレットをインポート";
"configureTransaction.data.label.title" = "トランザクション データ (オプション)";
"configureTransaction.nonce.label.title" = "Nonce (Optional)";
"confirmPayment.data.label.title" = "データ";
"confirmPayment.nonce.label.title" = "Nonce";
"Contract Address" = "契約アドレス";
"Copy Address" = "アドレスをコピー";
"Decimals" = "小数点";

@ -16,6 +16,7 @@
"Google" = "Google";
"configureTransaction.error.gasFeeTooHigh" = "가스 수수료가 너무 높습니다. 최대 이용 한도: %@";
"configureTransaction.error.gasLimitTooHigh" = "가스 한도가 너무 높습니다. 최대 이용 한도: %d";
"configureTransaction.error.nonceNotPositiveNumber" = "Nonce must be a positive number";
"configureTransaction.gasLimit.label.description" = "가스 한도는 스마트 계약이 %@을(를) 전부 소비하지 않도록 합니다. 가스 한도는 자동으로 계산되지만, 일부 스마트 계약은 사용자 정의 가스 한도가 필요할 수 있습니다.";
"configureTransaction.gasLimit.label.title" = "가스 한도";
"configureTransaction.gasPrice.label.description" = "가스 요금이 높을수록 거래 수수료는 더 비싸지지만, %@ 네트워크에서 거래는 더 빨리 처리됩니다.";
@ -27,6 +28,7 @@
"transaction.time.label.title" = "거래 시간";
"transaction.id.label.title" = "거래 번호";
"transaction.blockNumber.label.title" = "블록 번호";
"transaction.nonce.label.title" = "Nonce";
"confirmPayment.confirm.button.title" = "확인";
"confirmPayment.from.label.title" = "발신인";
"confirmPayment.gasFee.label.title" = "네트워크 수수료";
@ -126,7 +128,9 @@
"transaction.navigation.title" = "거래";
"import.navigation.title" = "지갑 가져오기";
"configureTransaction.data.label.title" = "거래 데이터 (선택 사항)";
"configureTransaction.nonce.label.title" = "Nonce (Optional)";
"confirmPayment.data.label.title" = "데이터";
"confirmPayment.nonce.label.title" = "Nonce";
"Contract Address" = "계약 주소";
"Copy Address" = "주소 복사";
"Decimals" = "소수점";

@ -16,6 +16,7 @@
"Google" = "Google";
"configureTransaction.error.gasFeeTooHigh" = "交易手续费用太高,最大可用:%@";
"configureTransaction.error.gasLimitTooHigh" = "燃料限制太高,最大可用:%d";
"configureTransaction.error.nonceNotPositiveNumber" = "Nonce must be a positive number";
"configureTransaction.gasLimit.label.description" = "燃料限制用于防止智能合约消耗你所有的%@。我们会尽可能得为你自动计算燃料限制,但是一些智能合约可能需要自定义的燃料限制。";
"configureTransaction.gasLimit.label.title" = "燃料限制";
"configureTransaction.gasPrice.label.description" = "燃料价格越高,交易费用就越贵,但是交易被 %@ 网络处理的速度越快。";
@ -114,7 +115,9 @@
"transaction.navigation.title" = "交易";
"import.navigation.title" = "导入钱包";
"configureTransaction.data.label.title" = "交易数据(可选)";
"configureTransaction.nonce.label.title" = "Nonce (Optional)";
"confirmPayment.data.label.title" = "数据";
"confirmPayment.nonce.label.title" = "Nonce";
"Contract Address" = "合约地址";
"Copy Address" = "复制地址";
"Decimals" = "位数";
@ -180,6 +183,7 @@
"transaction.time.label.title" = "交易时间";
"transaction.id.label.title" = "交易#";
"transaction.blockNumber.label.title" = "区块#";
"transaction.nonce.label.title" = "Nonce";
"send.error.invalidAmount" = "错误数额";
"wallet.create.button.title" = "创建钱包";
"wallet.watch.button.title" = "Watch Wallet";

@ -58,6 +58,7 @@ class TransactionViewController: UIViewController {
item(title: viewModel.transactionIDLabelTitle, value: viewModel.transactionID, icon: R.image.copy()),
item(title: viewModel.createdAtLabelTitle, value: viewModel.createdAt),
item(title: viewModel.blockNumberLabelTitle, value: viewModel.blockNumber),
item(title: viewModel.nonceLabelTitle, value: viewModel.nonce),
]
scrollView.translatesAutoresizingMaskIntoConstraints = false
@ -135,7 +136,7 @@ class TransactionViewController: UIViewController {
self?.copy(value: value, showHUD: icon != nil)
}
}
@objc func copy(value: String, showHUD: Bool = false) {
UIPasteboard.general.string = value

@ -54,7 +54,7 @@ struct TransactionDetailsViewModel {
var shareAvailable: Bool {
return detailsAvailable
}
var addressCopiedText: String {
return R.string.localizable.requestAddressCopiedTitle()
}
@ -62,7 +62,7 @@ struct TransactionDetailsViewModel {
var detailsURL: URL? {
return ConfigExplorer(server: server).transactionURL(for: transaction.id)?.url
}
var detailsButtonText: String {
if let name = ConfigExplorer(server: server).transactionURL(for: transaction.id)?.name {
return R.string.localizable.viewIn(name)
@ -140,6 +140,14 @@ struct TransactionDetailsViewModel {
return R.string.localizable.transactionBlockNumberLabelTitle()
}
var nonce: String {
String(transaction.nonce)
}
var nonceLabelTitle: String {
R.string.localizable.transactionNonceLabelTitle()
}
var amountAttributedString: NSAttributedString {
return transactionViewModel.fullAmountAttributedString
}

@ -105,7 +105,8 @@ class TransactionConfigurator {
strongSelf.configuration = TransactionConfiguration(
gasPrice: strongSelf.calculatedGasPrice,
gasLimit: gasLimit,
data: strongSelf.configuration.data
data: strongSelf.configuration.data,
nonce: strongSelf.configuration.nonce
)
case .failure: break
}
@ -118,7 +119,8 @@ class TransactionConfigurator {
strongSelf.configuration = TransactionConfiguration(
gasPrice: gasPrice,
gasLimit: strongSelf.transaction.gasLimit ?? GasLimitConfiguration.maxGasLimit,
data: strongSelf.configuration.data
data: strongSelf.configuration.data,
nonce: strongSelf.configuration.nonce
)
}
}
@ -164,14 +166,16 @@ class TransactionConfigurator {
configuration = TransactionConfiguration(
gasPrice: calculatedGasPrice,
gasLimit: GasLimitConfiguration.maxGasLimit,
data: transaction.data ?? configuration.data
data: transaction.data ?? configuration.data,
nonce: configuration.nonce
)
completion(.success(()))
case .nativeCryptocurrency:
configuration = TransactionConfiguration(
gasPrice: calculatedGasPrice,
gasLimit: GasLimitConfiguration.minGasLimit,
data: transaction.data ?? configuration.data
data: transaction.data ?? configuration.data,
nonce: configuration.nonce
)
completion(.success(()))
case .ERC20Token:
@ -292,7 +296,8 @@ class TransactionConfigurator {
account: account,
address: transaction.to,
contract: .none,
nonce: -1,
//TODO Can we make these `-1` for nonce be nil instead?
nonce: configuration.nonce ?? -1,
data: configuration.data,
gasPrice: configuration.gasPrice,
gasLimit: configuration.gasLimit,
@ -325,7 +330,7 @@ class TransactionConfigurator {
value: value,
account: account,
to: address,
nonce: -1,
nonce: configuration.nonce ?? -1,
data: configuration.data,
gasPrice: configuration.gasPrice,
gasLimit: configuration.gasLimit,

@ -6,6 +6,7 @@ import BigInt
enum ConfigureTransactionError: LocalizedError {
case gasLimitTooHigh
case gasFeeTooHigh
case nonceNotPositiveNumber
var errorDescription: String? {
switch self {
@ -13,6 +14,8 @@ enum ConfigureTransactionError: LocalizedError {
return R.string.localizable.configureTransactionErrorGasLimitTooHigh(ConfigureTransaction.gasLimitMax)
case .gasFeeTooHigh:
return R.string.localizable.configureTransactionErrorGasFeeTooHigh(String(ConfigureTransaction.gasFeeMax))
case .nonceNotPositiveNumber:
return R.string.localizable.configureTransactionErrorNonceNotPositiveNumber()
}
}
}

@ -7,4 +7,12 @@ struct TransactionConfiguration {
let gasPrice: BigInt
let gasLimit: BigInt
let data: Data
let nonce: Int?
init(gasPrice: BigInt, gasLimit: BigInt, data: Data, nonce: Int? = nil) {
self.gasPrice = gasPrice
self.gasLimit = gasLimit
self.data = data
self.nonce = nonce
}
}

@ -18,6 +18,7 @@ class ConfigureTransactionViewController: FormViewController {
private struct Values {
static let gasPrice = "gasPrice"
static let gasLimit = "gasLimit"
static let nonce = "nonce"
static let totalFee = "totalFee"
static let data = "data"
}
@ -28,6 +29,9 @@ class ConfigureTransactionViewController: FormViewController {
private var gasLimitRow: SliderTextFieldRow? {
return form.rowBy(tag: Values.gasLimit) as? SliderTextFieldRow
}
private var nonceRow: TextFloatLabelRow? {
return form.rowBy(tag: Values.nonce) as? TextFloatLabelRow
}
private var totalFeeRow: TextRow? {
return form.rowBy(tag: Values.totalFee) as? TextRow
}
@ -41,6 +45,9 @@ class ConfigureTransactionViewController: FormViewController {
private var gasPrice: BigInt {
return fullFormatter.number(from: String(Int(gasPriceRow?.value ?? 1)), units: UnitConfiguration.gasPriceUnit) ?? BigInt()
}
private var nonceString: String {
return nonceRow?.value?.trimmed ?? ""
}
private var totalFee: BigInt {
return gasPrice * gasLimit
}
@ -125,6 +132,16 @@ class ConfigureTransactionViewController: FormViewController {
}
}
+++ Section()
<<< AppFormAppearance.textFieldFloat(tag: Values.nonce) { [weak self] in
guard let strongSelf = self else { return }
$0.title = R.string.localizable.configureTransactionNonceLabelTitle()
$0.value = strongSelf.configuration.nonce.flatMap { String($0) }
}.cellUpdate { cell, row in
cell.textField.keyboardType = .numberPad
}
+++ Section {
$0.hidden = Eureka.Condition.function([], { [weak self] _ in
guard let strongSelf = self else { return true }
@ -162,6 +179,12 @@ class ConfigureTransactionViewController: FormViewController {
return displayError(error: ConfigureTransactionError.gasFeeTooHigh)
}
if !nonceString.isEmpty {
guard let nonce = Int(nonceString), nonce > 0 else {
return displayError(error: ConfigureTransactionError.nonceNotPositiveNumber)
}
}
let data: Data = {
if dataString.isEmpty {
return Data()
@ -169,10 +192,13 @@ class ConfigureTransactionViewController: FormViewController {
return Data(hex: dataString.drop0x)
}()
let nonce: Int? = Int(nonceString)
let configuration = TransactionConfiguration(
gasPrice: gasPrice,
gasLimit: gasLimit,
data: data
data: data,
nonce: nonce
)
delegate?.didEdit(configuration: configuration, in: self)
}

@ -124,6 +124,13 @@ class ConfirmPaymentViewController: UIViewController {
header.translatesAutoresizingMaskIntoConstraints = false
header.configure(amount: detailsViewModel.amountAttributedString)
let nonceRow = TransactionAppearance.item(
title: detailsViewModel.nonceTitle,
subTitle: detailsViewModel.nonceText
) { [unowned self] _, _, _ in
self.edit()
}
nonceRow.isHidden = !detailsViewModel.isNonceSet
let items: [UIView] = [
.spacer(),
header,
@ -154,6 +161,7 @@ class ConfirmPaymentViewController: UIViewController {
) { [unowned self] _, _, _ in
self.edit()
},
nonceRow,
TransactionAppearance.item(
title: detailsViewModel.dataTitle,
subTitle: detailsViewModel.dataText

@ -96,6 +96,18 @@ struct ConfirmPaymentDetailsViewModel {
return transaction.data.description
}
var nonceTitle: String {
return R.string.localizable.confirmPaymentNonceLabelTitle()
}
var nonceText: String {
transaction.nonce.description
}
var isNonceSet: Bool {
transaction.nonce > -1
}
var amountAttributedString: NSAttributedString {
switch transaction.transferType {
case .ERC20Token(let token, _, _):

Loading…
Cancel
Save