Skip to content

Commit e3fb69a

Browse files
Merge pull request #559 from mloit/experimental/async-auto-1559-gas
2 parents cc3d1ed + e0b2770 commit e3fb69a

File tree

1 file changed

+89
-40
lines changed

1 file changed

+89
-40
lines changed

Sources/web3swift/Web3/Web3+MutatingTransaction.swift

Lines changed: 89 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -62,48 +62,107 @@ public class WriteTransaction: ReadTransaction {
6262
optionsForGasEstimation.gasLimit = mergedOptions.gasLimit
6363
optionsForGasEstimation.callOnBlock = mergedOptions.callOnBlock
6464

65-
// assemble promise for gasLimit
66-
65+
// assemble gasLimit async call
66+
let assembledTransactionPostHood = assembledTransaction
67+
let optionsForGasEstimationPostHood = optionsForGasEstimation
6768
guard let gasLimitPolicy = mergedOptions.gasLimit else {
6869
throw Web3Error.inputError(desc: "No gasLimit policy provided")
6970
}
71+
async let gasEstimateAsync = gasEstimate(for: gasLimitPolicy, assembledTransaction: assembledTransactionPostHood, optionsForGasEstimation: optionsForGasEstimationPostHood)
7072

71-
guard let gasPricePolicy = mergedOptions.gasPrice else {
72-
throw Web3Error.inputError(desc: "No gasPrice policy provided")
73-
}
74-
73+
// assemble nonce async call
7574
guard let noncePolicy = mergedOptions.nonce else {
7675
throw Web3Error.inputError(desc: "No nonce policy provided")
7776
}
77+
async let getNonceAsync = nonce(for: noncePolicy, from: from)
78+
79+
// determine gas costing, taking transaction type into account
80+
let oracle = Web3.Oracle(self.web3, percentiles: [75])
81+
let finalGasPrice: BigUInt? // legacy gas model
82+
let finalGasFee: BigUInt? // EIP-1559 gas model
83+
let finalTipFee: BigUInt? // EIP-1559 gas model
84+
85+
if mergedOptions.type == nil || mergedOptions.type != .eip1559 { // legacy Gas
86+
// set unused gas parameters to nil
87+
finalGasFee = nil
88+
finalTipFee = nil
89+
90+
// determine the (legacy) gas price
91+
guard let gasPricePolicy = mergedOptions.gasPrice else {
92+
throw Web3Error.inputError(desc: "No gasPrice policy provided")
93+
}
94+
switch gasPricePolicy {
95+
case .automatic, .withMargin:
96+
let percentiles = await oracle.gasPriceLegacyPercentiles()
97+
guard !percentiles.isEmpty else {
98+
throw Web3Error.processingError(desc: "Failed to fetch gas price")
99+
}
100+
finalGasPrice = percentiles[0]
101+
case .manual(let gasPrice):
102+
finalGasPrice = gasPrice
103+
}
104+
} else { // else new gas fees (EIP-1559)
105+
// set unused gas parametes to nil
106+
finalGasPrice = nil
78107

108+
// determine the tip
109+
guard let maxPriorityFeePerGasPolicy = mergedOptions.maxPriorityFeePerGas else {
110+
throw Web3Error.inputError(desc: "No maxPriorityFeePerGas policy provided")
111+
}
112+
switch maxPriorityFeePerGasPolicy {
113+
case .automatic:
114+
let percentiles = await oracle.tipFeePercentiles()
115+
guard !percentiles.isEmpty else {
116+
throw Web3Error.processingError(desc: "Failed to fetch maxPriorityFeePerGas data")
117+
}
118+
finalTipFee = percentiles[0]
119+
case .manual(let maxPriorityFeePerGas):
120+
finalTipFee = maxPriorityFeePerGas
121+
}
79122

80-
let assembledTransactionPostHood = assembledTransaction
81-
let optionsForGasEstimationPostHood = optionsForGasEstimation
82-
83-
async let gasEstimatePromise = gasEstimate(for: gasLimitPolicy, assembledTransaction: assembledTransactionPostHood, optionsForGasEstimation: optionsForGasEstimationPostHood)
84-
85-
// assemble promise for nonce
86-
async let getNoncePromise = nonce(for: noncePolicy, from: from)
87-
88-
89-
// assemble promise for gasPrice
90-
async let gasPricePromise = gasPrice(for: gasPricePolicy)
91-
123+
// determine the baseFee, and calculate the maxFeePerGas
124+
guard let maxFeePerGasPolicy = mergedOptions.maxFeePerGas else {
125+
throw Web3Error.inputError(desc: "No maxFeePerGas policy provided")
126+
}
127+
switch maxFeePerGasPolicy {
128+
case .automatic:
129+
let percentiles = await oracle.baseFeePercentiles()
130+
guard !percentiles.isEmpty else {
131+
throw Web3Error.processingError(desc: "Failed to fetch baseFee data")
132+
}
133+
guard let tipFee = finalTipFee else {
134+
throw Web3Error.processingError(desc: "Missing tip value")
135+
}
136+
finalGasFee = percentiles[0] + tipFee
137+
case .manual(let maxFeePerGas):
138+
finalGasFee = maxFeePerGas
139+
}
140+
}
92141

93-
let results = try await [getNoncePromise, gasPricePromise, gasEstimatePromise]
142+
// wait for async calls to complete
143+
let results = try await [getNonceAsync, gasEstimateAsync]
94144

95145
let nonce = results[0]
96146
let gasEstimate = results[1]
97-
let gasPrice = results[2]
98-
99-
100-
let estimate = mergedOptions.resolveGasLimit(gasEstimate)
101-
let finalGasPrice = mergedOptions.resolveGasPrice(gasPrice)
102147

103148
var finalOptions = TransactionOptions()
149+
finalOptions.type = mergedOptions.type
104150
finalOptions.nonce = .manual(nonce)
105-
finalOptions.gasLimit = .manual(estimate)
106-
finalOptions.gasPrice = .manual(finalGasPrice)
151+
finalOptions.gasLimit = .manual(mergedOptions.resolveGasLimit(gasEstimate))
152+
finalOptions.accessList = mergedOptions.accessList
153+
154+
// set the finalized gas parameters
155+
if let gasPrice = finalGasPrice {
156+
finalOptions.gasPrice = .manual(mergedOptions.resolveGasPrice(gasPrice))
157+
}
158+
159+
if let tipFee = finalTipFee {
160+
finalOptions.maxPriorityFeePerGas = .manual(mergedOptions.resolveMaxPriorityFeePerGas(tipFee))
161+
}
162+
163+
if let gasFee = finalGasFee {
164+
finalOptions.maxFeePerGas = .manual(mergedOptions.resolveMaxFeePerGas(gasFee))
165+
}
107166

108167
assembledTransaction.applyOptions(finalOptions)
109168

@@ -120,9 +179,7 @@ public class WriteTransaction: ReadTransaction {
120179
}
121180
}
122181

123-
124182
return assembledTransaction
125-
126183
}
127184

128185
public func send(password: String = "web3swift", transactionOptions: TransactionOptions? = nil) async throws -> TransactionSendingResult {
@@ -138,8 +195,9 @@ public class WriteTransaction: ReadTransaction {
138195
return try await self.assembleTransaction(transactionOptions: transactionOptions)
139196
}
140197

141-
func gasEstimate(for policy: TransactionOptions.GasLimitPolicy
142-
, assembledTransaction: EthereumTransaction, optionsForGasEstimation: TransactionOptions) async throws -> BigUInt {
198+
func gasEstimate(for policy: TransactionOptions.GasLimitPolicy,
199+
assembledTransaction: EthereumTransaction,
200+
optionsForGasEstimation: TransactionOptions) async throws -> BigUInt {
143201
switch policy {
144202
case .automatic, .withMargin, .limited:
145203
return try await self.web3.eth.estimateGas(for: assembledTransaction, transactionOptions: optionsForGasEstimation)
@@ -148,7 +206,7 @@ public class WriteTransaction: ReadTransaction {
148206
}
149207
}
150208

151-
func nonce(for policy: TransactionOptions.NoncePolicy, from: EthereumAddress) async throws -> BigUInt {
209+
func nonce(for policy: TransactionOptions.NoncePolicy, from: EthereumAddress) async throws -> BigUInt {
152210
switch policy {
153211
case .latest:
154212
return try await self.web3.eth.getTransactionCount(address: from, onBlock: "latest")
@@ -158,13 +216,4 @@ public class WriteTransaction: ReadTransaction {
158216
return nonce
159217
}
160218
}
161-
162-
func gasPrice(for policy: TransactionOptions.GasPricePolicy) async throws -> BigUInt {
163-
switch policy {
164-
case .automatic, .withMargin:
165-
return try await self.web3.eth.gasPrice()
166-
case .manual(let gasPrice):
167-
return gasPrice
168-
}
169-
}
170219
}

0 commit comments

Comments
 (0)