Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Evm send improvements #5826

Merged
merged 2 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2836,6 +2836,8 @@
D02A67D8272A7460009B2C1C /* CoinTweetsModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02A67BB272A7460009B2C1C /* CoinTweetsModule.swift */; };
D033289F2BF6199600BBB364 /* InfoNewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033289E2BF6199600BBB364 /* InfoNewView.swift */; };
D03328A02BF6199600BBB364 /* InfoNewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033289E2BF6199600BBB364 /* InfoNewView.swift */; };
D03F74822BF76D0A004FBCFA /* GasPriceData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03F74812BF76D0A004FBCFA /* GasPriceData.swift */; };
D03F74832BF76D0A004FBCFA /* GasPriceData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03F74812BF76D0A004FBCFA /* GasPriceData.swift */; };
D04D98EA268055A2001A3135 /* TransactionRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04D98E9268055A2001A3135 /* TransactionRecord.swift */; };
D04D98EB268055A2001A3135 /* TransactionRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04D98E9268055A2001A3135 /* TransactionRecord.swift */; };
D04DD5412B68C0EF00219B87 /* HighlightedTiltledTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04DD5402B68C0EF00219B87 /* HighlightedTiltledTextView.swift */; };
Expand Down Expand Up @@ -4818,6 +4820,7 @@
D02A67BA272A7460009B2C1C /* CoinTweetsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoinTweetsService.swift; sourceTree = "<group>"; };
D02A67BB272A7460009B2C1C /* CoinTweetsModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoinTweetsModule.swift; sourceTree = "<group>"; };
D033289E2BF6199600BBB364 /* InfoNewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoNewView.swift; sourceTree = "<group>"; };
D03F74812BF76D0A004FBCFA /* GasPriceData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GasPriceData.swift; sourceTree = "<group>"; };
D04D98E9268055A2001A3135 /* TransactionRecord.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionRecord.swift; sourceTree = "<group>"; };
D04DD5402B68C0EF00219B87 /* HighlightedTiltledTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightedTiltledTextView.swift; sourceTree = "<group>"; };
D0532CBD2B149DEE0015DF40 /* WatchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -9260,6 +9263,7 @@
D31369882BEA188D00BA6B5B /* ZcashPreSendHandler.swift */,
D084F6BD2BEB94F700407FA4 /* OutputSelectView2.swift */,
D084F6C02BEB951C00407FA4 /* OutputSelectorViewModel2.swift */,
D03F74812BF76D0A004FBCFA /* GasPriceData.swift */,
);
path = SendNew;
sourceTree = "<group>";
Expand Down Expand Up @@ -10756,6 +10760,7 @@
2FA5D68102F8CD6FB793A158 /* EvmSendSettingsModule.swift in Sources */,
2FA5D4C40B723434DB9D2DBE /* EvmSendSettingsService.swift in Sources */,
2FA5D987AC6D91F7929DE933 /* EvmSendSettingsViewController.swift in Sources */,
D03F74832BF76D0A004FBCFA /* GasPriceData.swift in Sources */,
D05E96AA2A28657F002CCD71 /* TronAccountManager.swift in Sources */,
2FA5D61F4FA6E818D25C4A96 /* EvmSendSettingsViewModel.swift in Sources */,
2FA5D201AED8FB83968A5220 /* StepperAmountInputView.swift in Sources */,
Expand Down Expand Up @@ -12318,6 +12323,7 @@
11B35CA6259EDA3708695416 /* FaqUrlHelper.swift in Sources */,
11B3534B12C5E7596E4953F0 /* RestoreSettingsModule.swift in Sources */,
2FA5DF494C667E041B22C804 /* EvmSendSettingsModule.swift in Sources */,
D03F74822BF76D0A004FBCFA /* GasPriceData.swift in Sources */,
D05E96A92A28657F002CCD71 /* TronAccountManager.swift in Sources */,
2FA5DBD32AA258A75A2FDD95 /* EvmSendSettingsService.swift in Sources */,
2FA5D7C86A2A55B906953B76 /* EvmSendSettingsViewController.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ class BaseUniswapMultiSwapProvider: BaseEvmMultiSwapProvider {
let quote = try await internalQuote(tokenIn: tokenIn, tokenOut: tokenOut, amountIn: amountIn)

let blockchainType = tokenIn.blockchainType
let gasPrice = transactionSettings?.gasPrice
let gasPriceData = transactionSettings?.gasPriceData
var txData: TransactionData?
var evmFeeData: EvmFeeData?
var transactionError: Error?

if let evmKitWrapper = evmBlockchainManager.evmKitManager(blockchainType: blockchainType).evmKitWrapper, let gasPrice {
if let evmKitWrapper = evmBlockchainManager.evmKitManager(blockchainType: blockchainType).evmKitWrapper, let gasPriceData {
do {
let evmKit = evmKitWrapper.evmKit
let transactionData = try transactionData(receiveAddress: evmKit.receiveAddress, chain: evmKit.chain, trade: quote.trade, tradeOptions: quote.tradeOptions)
txData = transactionData
evmFeeData = try await evmFeeEstimator.estimateFee(evmKitWrapper: evmKitWrapper, transactionData: transactionData, gasPrice: gasPrice)
evmFeeData = try await evmFeeEstimator.estimateFee(evmKitWrapper: evmKitWrapper, transactionData: transactionData, gasPriceData: gasPriceData)
} catch {
transactionError = error
}
Expand All @@ -37,7 +37,7 @@ class BaseUniswapMultiSwapProvider: BaseEvmMultiSwapProvider {
quote: quote,
transactionData: txData,
transactionError: transactionError,
gasPrice: gasPrice,
gasPrice: gasPriceData?.userDefined,
evmFeeData: evmFeeData,
nonce: transactionSettings?.nonce
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ class OneInchMultiSwapProvider: BaseEvmMultiSwapProvider {
let quote = try await internalQuote(tokenIn: tokenIn, tokenOut: tokenOut, amountIn: amountIn)

let blockchainType = tokenIn.blockchainType
let gasPrice = transactionSettings?.gasPrice
let gasPriceData = transactionSettings?.gasPriceData
var evmFeeData: EvmFeeData?
var resolvedSwap: Swap?
var insufficientFeeBalance = false

if let evmKitWrapper = evmBlockchainManager.evmKitManager(blockchainType: blockchainType).evmKitWrapper,
let gasPrice,
let gasPriceData,
let amount = rawAmount(amount: amountIn, token: tokenIn)
{
let evmKit = evmKitWrapper.evmKit
Expand All @@ -71,22 +71,22 @@ class OneInchMultiSwapProvider: BaseEvmMultiSwapProvider {
referrer: commissionAddress,
fee: commission,
recipient: storage.recipient(blockchainType: blockchainType).flatMap { try? EvmKit.Address(hex: $0.raw) },
gasPrice: gasPrice
gasPrice: gasPriceData.userDefined
)

resolvedSwap = swap

let evmBalance = evmKit.accountState?.balance ?? 0
let txAmount = swap.transaction.value
let feeAmount = BigUInt(swap.transaction.gasLimit * gasPrice.max)
let feeAmount = BigUInt(swap.transaction.gasLimit * gasPriceData.userDefined.max)
let totalAmount = txAmount + feeAmount

insufficientFeeBalance = totalAmount > evmBalance

evmFeeData = try await evmFeeEstimator.estimateFee(
evmKitWrapper: evmKitWrapper,
transactionData: swap.transactionData,
gasPrice: gasPrice,
gasPriceData: gasPriceData,
predefinedGasLimit: swap.transaction.gasLimit
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,13 @@ class ThorChainMultiSwapProvider: IMultiSwapProvider {
}

let blockchainType = tokenIn.blockchainType
let gasPrice = transactionSettings?.gasPrice
let gasPriceData = transactionSettings?.gasPriceData
var evmFeeData: EvmFeeData?
var transactionError: Error?

if let evmKitWrapper = evmBlockchainManager.evmKitManager(blockchainType: blockchainType).evmKitWrapper, let gasPrice {
if let evmKitWrapper = evmBlockchainManager.evmKitManager(blockchainType: blockchainType).evmKitWrapper, let gasPriceData {
do {
evmFeeData = try await evmFeeEstimator.estimateFee(evmKitWrapper: evmKitWrapper, transactionData: transactionData, gasPrice: gasPrice)
evmFeeData = try await evmFeeEstimator.estimateFee(evmKitWrapper: evmKitWrapper, transactionData: transactionData, gasPriceData: gasPriceData)
} catch {
transactionError = error
}
Expand All @@ -136,7 +136,7 @@ class ThorChainMultiSwapProvider: IMultiSwapProvider {
slippage: slippage,
transactionData: transactionData,
transactionError: transactionError,
gasPrice: gasPrice,
gasPrice: gasPriceData?.userDefined,
evmFeeData: evmFeeData,
nonce: transactionSettings?.nonce
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import MarketKit
struct EvmFeeEstimator {
private static let surchargePercent: Double = 10

func estimateFee(evmKitWrapper: EvmKitWrapper, transactionData: TransactionData, gasPrice: GasPrice, predefinedGasLimit: Int? = nil) async throws -> EvmFeeData {
private func _estimateFee(evmKitWrapper: EvmKitWrapper, transactionData: TransactionData, gasPrice: GasPrice, predefinedGasLimit: Int? = nil) async throws -> EvmFeeData {
let evmKit = evmKitWrapper.evmKit
let gasLimit: Int

Expand Down Expand Up @@ -48,4 +48,16 @@ struct EvmFeeEstimator {

return .init(gasLimit: gasLimit, surchargedGasLimit: surchargedGasLimit, l1Fee: l1Fee)
}

func estimateFee(evmKitWrapper: EvmKitWrapper, transactionData: TransactionData, gasPriceData: GasPriceData, predefinedGasLimit: Int? = nil) async throws -> EvmFeeData {
do {
return try await _estimateFee(evmKitWrapper: evmKitWrapper, transactionData: transactionData, gasPrice: gasPriceData.userDefined)
} catch {
if case let AppError.ethereum(reason: ethereumError) = error.convertedError, case .lowerThanBaseGasLimit = ethereumError {
return try await _estimateFee(evmKitWrapper: evmKitWrapper, transactionData: transactionData, gasPrice: gasPriceData.recommended)
} else {
throw error
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,24 @@ extension EvmSendHandler: ISendHandler {
}

func sendData(transactionSettings: TransactionSettings?) async throws -> ISendData {
let gasPrice = transactionSettings?.gasPrice
let gasPriceData = transactionSettings?.gasPriceData
var evmFeeData: EvmFeeData?
var transactionError: Error?

var transactionData = transactionData

if let gasPrice {
if let gasPriceData {
let evmBalance = evmKitWrapper.evmKit.accountState?.balance ?? 0

do {
if transactionData.input.isEmpty, transactionData.value == evmBalance {
let stubTransactionData = TransactionData(to: transactionData.to, value: 1, input: transactionData.input)
let stubFeeData = try await evmFeeEstimator.estimateFee(evmKitWrapper: evmKitWrapper, transactionData: stubTransactionData, gasPrice: gasPrice)
let totalFee = stubFeeData.totalFee(gasPrice: gasPrice)
let stubFeeData = try await evmFeeEstimator.estimateFee(evmKitWrapper: evmKitWrapper, transactionData: stubTransactionData, gasPriceData: gasPriceData)
let totalFee = stubFeeData.totalFee(gasPrice: gasPriceData.userDefined)

evmFeeData = stubFeeData
transactionData = TransactionData(to: transactionData.to, value: max(0, transactionData.value - totalFee), input: transactionData.input)
} else {
evmFeeData = try await evmFeeEstimator.estimateFee(evmKitWrapper: evmKitWrapper, transactionData: transactionData, gasPrice: gasPrice)
evmFeeData = try await evmFeeEstimator.estimateFee(evmKitWrapper: evmKitWrapper, transactionData: transactionData, gasPriceData: gasPriceData)
}
} catch {
transactionError = error
Expand All @@ -54,7 +53,7 @@ extension EvmSendHandler: ISendHandler {
decoration: decoration,
transactionData: transactionData,
transactionError: transactionError,
gasPrice: gasPrice,
gasPrice: gasPriceData?.userDefined,
evmFeeData: evmFeeData,
nonce: transactionSettings?.nonce
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,14 @@ class EvmTransactionService: ITransactionService {
}

var transactionSettings: TransactionSettings? {
guard let gasPrice else {
guard let gasPrice, let recommendedGasPrice else {
return nil
}

return .evm(gasPrice: gasPrice, nonce: usingRecommendedNonce ? nil : nonce)
return .evm(
gasPriceData: GasPriceData(recommended: recommendedGasPrice, userDefined: gasPrice),
nonce: usingRecommendedNonce ? nil : nonce
)
}

init?(blockchainType: BlockchainType, userAddress: EvmKit.Address, initialTransactionSettings: InitialTransactionSettings?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,50 @@ class Eip1559FeeSettingsViewModel: ObservableObject {
baseFee = feeViewItemFactory.description(value: recommendedMaxFeePerGas, step: baseStep)
}

sync()
syncFromService()
}

@Published var baseFee: String = ""
@Published var maxFeeCautionState: FieldCautionState = .none
@Published var maxFee: String = ""
@Published var maxFee: String = "" {
didSet {
DispatchQueue.main.async { [weak self] in
self?.handleGasPrice()
}
}
}

@Published var maxTipsCautionState: FieldCautionState = .none
@Published var maxTips: String = ""
@Published var maxTips: String = "" {
didSet {
DispatchQueue.main.async { [weak self] in
self?.handleGasPrice()
}
}
}

@Published var nonceCautionState: FieldCautionState = .none
@Published var nonce: String = ""
@Published var nonce: String = "" {
didSet {
DispatchQueue.main.async { [weak self] in
self?.handleNonce()
}
}
}

@Published var resetEnabled = false

private func sync() {
private func syncFromService() {
if case let .eip1559(maxFee, maxTips) = service.gasPrice {
self.maxFee = feeViewItemFactory.decimalValue(value: maxFee).description
self.maxTips = feeViewItemFactory.decimalValue(value: maxTips).description
}

nonce = "\(service.nonce ?? 0)"
sync()
}

private func sync() {
resetEnabled = service.modified

if service.warnings.contains(where: { $0 is EvmFeeModule.GasDataWarning }) {
Expand Down Expand Up @@ -65,7 +90,6 @@ class Eip1559FeeSettingsViewModel: ObservableObject {
maxFeePerGas: feeViewItemFactory.intValue(value: maxFeeDecimal),
maxPriorityFeePerGas: feeViewItemFactory.intValue(value: maxTipsDecimal)
))

sync()
}

Expand Down Expand Up @@ -95,26 +119,23 @@ extension Eip1559FeeSettingsViewModel {
func stepChangeMaxFee(_ direction: StepChangeButtonsViewDirection) {
if let newValue = updateByStep(value: maxFee, direction: direction) {
maxFee = newValue.description
handleGasPrice()
}
}

func stepChangeMaxTips(_ direction: StepChangeButtonsViewDirection) {
if let newValue = updateByStep(value: maxTips, direction: direction) {
maxTips = newValue.description
handleGasPrice()
}
}

func stepChangeNonce(_ direction: StepChangeButtonsViewDirection) {
if let newValue = updateByStep(value: nonce, direction: direction) {
nonce = newValue.description
handleNonce()
}
}

func onReset() {
service.useRecommended()
sync()
syncFromService()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,38 @@ class LegacyFeeSettingsViewModel: ObservableObject {
self.service = service
self.feeViewItemFactory = feeViewItemFactory

sync()
syncFromService()
}

@Published var gasPriceCautionState: FieldCautionState = .none
@Published var gasPrice: String = ""
@Published var gasPrice: String = "" {
didSet {
DispatchQueue.main.async { [weak self] in
self?.handleGasPrice()
}
}
}

@Published var nonceCautionState: FieldCautionState = .none
@Published var nonce: String = ""
@Published var nonce: String = "" {
didSet {
DispatchQueue.main.async { [weak self] in
self?.handleNonce()
}
}
}

@Published var resetEnabled = false

private func sync() {
private func syncFromService() {
if case let .legacy(gasPrice) = service.gasPrice {
self.gasPrice = feeViewItemFactory.decimalValue(value: gasPrice).description
}

nonce = "\(service.nonce ?? 0)"
sync()
}

private func sync() {
resetEnabled = service.modified

if service.warnings.contains(where: { $0 is EvmFeeModule.GasDataWarning }) {
Expand Down Expand Up @@ -77,19 +94,17 @@ extension LegacyFeeSettingsViewModel {
func stepChangeGasPrice(_ direction: StepChangeButtonsViewDirection) {
if let newValue = updateByStep(value: gasPrice, direction: direction) {
gasPrice = newValue.description
handleGasPrice()
}
}

func stepChangeNonce(_ direction: StepChangeButtonsViewDirection) {
if let newValue = updateByStep(value: nonce, direction: direction) {
nonce = newValue.description
handleNonce()
}
}

func onReset() {
service.useRecommended()
sync()
syncFromService()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import EvmKit

struct GasPriceData {
let recommended: GasPrice
let userDefined: GasPrice
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct RegularSendView: View {
case .syncing:
EmptyView()
case let .success(data):
if sendViewModel.handler?.expirationDuration == nil || sendViewModel.timeLeft > 0 || sendViewModel.sending {
if data.canSend, sendViewModel.handler?.expirationDuration == nil || sendViewModel.timeLeft > 0 || sendViewModel.sending {
SlideButton(
styling: .text(start: data.customSendButtonTitle ?? "send.confirmation.slide_to_send".localized, end: "", success: ""),
action: {
Expand Down
Loading