Skip to content

Commit 4b9c261

Browse files
Merge pull request #645 from janndriessen/feat/add-policy-resolver
2 parents 5694b64 + 9fad6b2 commit 4b9c261

24 files changed

+316
-221
lines changed

Sources/Core/Structure/Block/BlockNumber.swift

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,3 @@ extension BlockNumber: APIRequestParameterType {
3939
try container.encode(description)
4040
}
4141
}
42-
43-
extension BlockNumber: Policyable {
44-
public func resolve(provider: Web3Provider, transaction: CodableTransaction?) async throws -> BigUInt {
45-
guard let transaction = transaction else { throw Web3Error.valueError }
46-
switch self {
47-
case .pending, .latest, .earliest:
48-
guard let address = transaction.from ?? transaction.sender else { throw Web3Error.valueError }
49-
let request: APIRequest = .getTransactionCount(address.address, transaction.callOnBlock ?? .latest)
50-
let response: APIResponse<BigUInt> = try await APIRequest.sendRequest(with: provider, for: request)
51-
return response.result
52-
case .exact(let value):
53-
return value
54-
}
55-
}
56-
}

Sources/Core/Structure/Web3ProviderProtocol.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Foundation
1010
public protocol Web3Provider {
1111
var network: Networks? {get set}
1212
var attachedKeystoreManager: KeystoreManager? {get set}
13+
var policies: Policies {get set}
1314
var url: URL {get}
1415
var session: URLSession {get}
1516
}

Sources/Core/Transaction/CodableTransaction.swift

Lines changed: 8 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@
77
import Foundation
88
import BigInt
99

10-
/// Structure capable of carying the parameters for any transaction type.
11-
/// while all fields in this struct are optional, they are not necessarily
12-
/// optional for the type of transaction they apply to.
10+
/// Structure capable of carying the parameters for any transaction type.
11+
/// While most fields in this struct are optional, they are not necessarily
12+
/// optional for the type of transaction they apply to.
1313
public struct CodableTransaction {
14-
public typealias NoncePolicy = BlockNumber
1514
/// internal acccess only. The transaction envelope object itself that contains all the transaction data
1615
/// and type specific implementation
1716
internal var envelope: AbstractEnvelope
@@ -62,32 +61,32 @@ public struct CodableTransaction {
6261
// MARK: - Properties transaction type related either sends to a node if exist
6362

6463
/// the nonce for the transaction
65-
internal var nonce: BigUInt {
64+
public var nonce: BigUInt {
6665
get { return envelope.nonce }
6766
set { envelope.nonce = newValue }
6867
}
6968

7069
/// the max number of gas units allowed to process this transaction
71-
internal var gasLimit: BigUInt {
70+
public var gasLimit: BigUInt {
7271
get { return envelope.gasLimit }
7372
set { return envelope.gasLimit = newValue }
7473
}
7574

7675
/// the price per gas unit for the tranaction (Legacy and EIP-2930 only)
77-
internal var gasPrice: BigUInt? {
76+
public var gasPrice: BigUInt? {
7877
get { return envelope.gasPrice }
7978
set { return envelope.gasPrice = newValue }
8079
}
8180

8281
/// the max base fee per gas unit (EIP-1559 only)
8382
/// this value must be >= baseFee + maxPriorityFeePerGas
84-
internal var maxFeePerGas: BigUInt? {
83+
public var maxFeePerGas: BigUInt? {
8584
get { return envelope.maxFeePerGas }
8685
set { return envelope.maxFeePerGas = newValue }
8786
}
8887

8988
/// the maximum tip to pay the miner (EIP-1559 only)
90-
internal var maxPriorityFeePerGas: BigUInt? {
89+
public var maxPriorityFeePerGas: BigUInt? {
9190
get { return envelope.maxPriorityFeePerGas }
9291
set { return envelope.maxPriorityFeePerGas = newValue }
9392
}
@@ -175,39 +174,13 @@ public struct CodableTransaction {
175174
self.envelope = env
176175
// FIXME: This is duplication and should be fixed.
177176
data = Data()
178-
noncePolicy = .latest
179-
gasLimitPolicy = .automatic
180-
gasPricePolicy = .automatic
181-
maxFeePerGasPolicy = .automatic
182-
maxPriorityFeePerGasPolicy = .automatic
183177
}
184178

185179
/// - Returns: a raw bytestream of the transaction, encoded according to the transactionType
186180
public func encode(for type: EncodeType = .transaction) -> Data? {
187181
return self.envelope.encode(for: type)
188182
}
189183

190-
public mutating func resolve(provider: Web3Provider) async {
191-
// FIXME: Delete force try
192-
self.gasLimit = try! await self.gasLimitPolicy.resolve(provider: provider, transaction: self)
193-
194-
if from != nil || sender != nil {
195-
self.nonce = try! await self.resolveNonce(provider: provider)
196-
}
197-
if case .eip1559 = type {
198-
self.maxFeePerGas = try! await self.maxFeePerGasPolicy.resolve(provider: provider)
199-
self.maxPriorityFeePerGas = try! await self.maxPriorityFeePerGasPolicy.resolve(provider: provider)
200-
} else {
201-
self.gasPrice = try! await self.gasPricePolicy.resolve(provider: provider)
202-
}
203-
}
204-
205-
public var noncePolicy: NoncePolicy
206-
public var maxFeePerGasPolicy: FeePerGasPolicy
207-
public var maxPriorityFeePerGasPolicy: PriorityFeePerGasPolicy
208-
public var gasPricePolicy: GasPricePolicy
209-
public var gasLimitPolicy: GasLimitPolicy
210-
211184
public static var emptyTransaction = CodableTransaction(to: EthereumAddress.contractDeploymentAddress())
212185
}
213186

@@ -235,12 +208,6 @@ extension CodableTransaction: Codable {
235208
// FIXME: This is duplication and should be fixed.
236209
data = Data()
237210

238-
noncePolicy = .latest
239-
gasLimitPolicy = .automatic
240-
gasPricePolicy = .automatic
241-
maxFeePerGasPolicy = .automatic
242-
maxPriorityFeePerGasPolicy = .automatic
243-
244211
// capture any metadata that might be present
245212
self.meta = try TransactionMetadata(from: decoder)
246213
}
@@ -292,96 +259,6 @@ extension CodableTransaction: Codable {
292259

293260
}
294261

295-
public protocol Policyable {
296-
func resolve(provider: Web3Provider, transaction: CodableTransaction?) async throws -> BigUInt
297-
}
298-
299-
extension CodableTransaction {
300-
public enum GasLimitPolicy {
301-
case automatic
302-
case manual(BigUInt)
303-
case limited(BigUInt)
304-
case withMargin(Double)
305-
306-
func resolve(provider: Web3Provider, transaction: CodableTransaction?) async throws -> BigUInt {
307-
guard let transaction = transaction else { throw Web3Error.valueError }
308-
let request: APIRequest = .estimateGas(transaction, transaction.callOnBlock ?? .latest)
309-
let response: APIResponse<BigUInt> = try await APIRequest.sendRequest(with: provider, for: request)
310-
switch self {
311-
case .automatic, .withMargin:
312-
return response.result
313-
case .manual(let value):
314-
return value
315-
case .limited(let limit):
316-
if limit <= response.result {
317-
return response.result
318-
} else {
319-
return limit
320-
}
321-
}
322-
}
323-
}
324-
325-
public enum GasPricePolicy {
326-
case automatic
327-
case manual(BigUInt)
328-
case withMargin(Double)
329-
330-
func resolve(provider: Web3Provider, transaction: CodableTransaction? = nil) async throws -> BigUInt {
331-
let oracle = Oracle(provider)
332-
switch self {
333-
case .automatic, .withMargin:
334-
return await oracle.gasPriceLegacyPercentiles().max() ?? 0
335-
case .manual(let value):
336-
return value
337-
}
338-
}
339-
}
340-
341-
public enum PriorityFeePerGasPolicy: Policyable {
342-
case automatic
343-
case manual(BigUInt)
344-
345-
public func resolve(provider: Web3Provider, transaction: CodableTransaction? = nil) async throws -> BigUInt {
346-
let oracle = Oracle(provider)
347-
switch self {
348-
case .automatic:
349-
return await oracle.tipFeePercentiles().max() ?? 0
350-
case .manual(let value):
351-
return value
352-
}
353-
}
354-
}
355-
356-
public enum FeePerGasPolicy: Policyable {
357-
case automatic
358-
case manual(BigUInt)
359-
360-
public func resolve(provider: Web3Provider, transaction: CodableTransaction? = nil) async throws -> BigUInt {
361-
let oracle = Oracle(provider)
362-
switch self {
363-
case .automatic:
364-
return await oracle.baseFeePercentiles().max() ?? 0
365-
case .manual(let value):
366-
return value
367-
}
368-
}
369-
}
370-
371-
func resolveNonce(provider: Web3Provider) async throws -> BigUInt {
372-
switch noncePolicy {
373-
case .pending, .latest, .earliest:
374-
guard let address = from ?? sender else { throw Web3Error.valueError }
375-
let request: APIRequest = .getTransactionCount(address.address, callOnBlock ?? .latest)
376-
let response: APIResponse<BigUInt> = try await APIRequest.sendRequest(with: provider, for: request)
377-
return response.result
378-
case .exact(let value):
379-
return value
380-
}
381-
}
382-
383-
}
384-
385262
extension CodableTransaction: CustomStringConvertible {
386263
/// required by CustomString convertable
387264
/// returns a string description for the transaction and its data
@@ -416,11 +293,6 @@ extension CodableTransaction {
416293
// FIXME: This is duplication and should be fixed.
417294
self.data = data
418295
self.accessList = accessList
419-
self.gasLimitPolicy = .automatic
420-
self.noncePolicy = .pending
421-
self.gasPricePolicy = .automatic
422-
self.maxFeePerGasPolicy = .automatic
423-
self.maxPriorityFeePerGasPolicy = .automatic
424296
self.callOnBlock = .latest
425297

426298
self.envelope = EnvelopeFactory.createEnvelope(type: type, to: to, nonce: nonce, chainID: chainID, value: value, data: data, gasLimit: gasLimit, maxFeePerGas: maxFeePerGas, maxPriorityFeePerGas: maxPriorityFeePerGas, gasPrice: gasPrice, accessList: accessList, v: v, r: r, s: s)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// Policies.swift
3+
//
4+
//
5+
// Created by Jann Driessen on 01.11.22.
6+
//
7+
8+
import Foundation
9+
import BigInt
10+
11+
public typealias NoncePolicy = BlockNumber
12+
13+
public enum GasLimitPolicy {
14+
case automatic
15+
case manual(BigUInt)
16+
case limited(BigUInt)
17+
case withMargin(Double)
18+
}
19+
20+
public enum GasPricePolicy {
21+
case automatic
22+
case manual(BigUInt)
23+
case withMargin(Double)
24+
}
25+
26+
public enum FeePerGasPolicy {
27+
case automatic
28+
case manual(BigUInt)
29+
}
30+
31+
public enum PriorityFeePerGasPolicy {
32+
case automatic
33+
case manual(BigUInt)
34+
}
35+
36+
public struct Policies {
37+
public let noncePolicy: NoncePolicy
38+
public let gasLimitPolicy: GasLimitPolicy
39+
public let gasPricePolicy: GasPricePolicy
40+
public let maxFeePerGasPolicy: FeePerGasPolicy
41+
public let maxPriorityFeePerGasPolicy: PriorityFeePerGasPolicy
42+
43+
public init(
44+
noncePolicy: NoncePolicy = .latest,
45+
gasLimitPolicy: GasLimitPolicy = .automatic,
46+
gasPricePolicy: GasPricePolicy = .automatic,
47+
maxFeePerGasPolicy: FeePerGasPolicy = .automatic,
48+
maxPriorityFeePerGasPolicy: PriorityFeePerGasPolicy = .automatic) {
49+
self.noncePolicy = noncePolicy
50+
self.gasLimitPolicy = gasLimitPolicy
51+
self.gasPricePolicy = gasPricePolicy
52+
self.maxFeePerGasPolicy = maxFeePerGasPolicy
53+
self.maxPriorityFeePerGasPolicy = maxPriorityFeePerGasPolicy
54+
}
55+
56+
public static var auto: Policies {
57+
return Policies(
58+
noncePolicy: .latest,
59+
gasLimitPolicy: .automatic,
60+
gasPricePolicy: .automatic,
61+
maxFeePerGasPolicy: .automatic,
62+
maxPriorityFeePerGasPolicy: .automatic
63+
)
64+
}
65+
}

Sources/web3swift/Operations/ReadTransaction.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class ReadOperation {
1616
public var method: String
1717
public var data: Data? { transaction.data }
1818

19+
var resolver: PolicyResolver
1920
var web3: Web3
2021

2122
// FIXME: Rewrite this to CodableTransaction
@@ -30,11 +31,12 @@ public class ReadOperation {
3031
if let network = self.web3.provider.network {
3132
self.transaction.chainID = network.chainID
3233
}
34+
self.resolver = PolicyResolver(provider: web3.provider)
3335
}
3436

3537
// TODO: Remove type erasing here, some broad wide protocol should be added instead
3638
public func callContractMethod() async throws -> [String: Any] {
37-
await transaction.resolve(provider: web3.provider)
39+
try await resolver.resolveAll(for: &transaction)
3840
// MARK: Read data from ABI flow
3941
// FIXME: This should be dropped, and after `execute()` call, just to decode raw data.
4042
let data: Data = try await self.web3.eth.callTransaction(transaction)

Sources/web3swift/Operations/WriteOperation.swift

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ import Core
1111
public class WriteOperation: ReadOperation {
1212

1313
// FIXME: Rewrite this to CodableTransaction
14-
public func writeToChain(password: String) async throws -> TransactionSendingResult {
15-
await transaction.resolve(provider: web3.provider)
14+
/// Sends (raw) transaction for write operation.
15+
/// - Parameters:
16+
/// - password: Password for private key.
17+
/// - policies: Custom policies for how to resolve (optional). Default is auto.
18+
public func writeToChain(password: String, policies: Policies = .auto) async throws -> TransactionSendingResult {
19+
try await resolver.resolveAll(for: &transaction, with: policies)
1620
if let attachedKeystoreManager = self.web3.provider.attachedKeystoreManager {
1721
do {
1822
try Web3Signer.signTX(transaction: &transaction,
@@ -28,18 +32,4 @@ public class WriteOperation: ReadOperation {
2832
// MARK: Sending Data flow
2933
return try await web3.eth.send(transaction)
3034
}
31-
32-
// FIXME: Rewrite this to CodableTransaction
33-
func nonce(for policy: CodableTransaction.NoncePolicy, from: EthereumAddress) async throws -> BigUInt {
34-
switch policy {
35-
case .latest:
36-
return try await self.web3.eth.getTransactionCount(for: from, onBlock: .latest)
37-
case .pending:
38-
return try await self.web3.eth.getTransactionCount(for: from, onBlock: .pending)
39-
case .earliest:
40-
return try await self.web3.eth.getTransactionCount(for: from, onBlock: .earliest)
41-
case .exact(let nonce):
42-
return nonce
43-
}
44-
}
4535
}

Sources/web3swift/Tokens/ERC1155/Web3+ERC1155.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ public class ERC1155: IERC1155 {
136136
public func supportsInterface(interfaceID: String) async throws -> Bool {
137137
let contract = self.contract
138138
self.transaction.callOnBlock = .latest
139-
self.transaction.gasLimitPolicy = .manual(30000)
140139
let result = try await contract.createReadOperation("supportsInterface", parameters: [interfaceID] as [AnyObject], extraData: Data() )!.callContractMethod()
141140
guard let res = result["0"] as? Bool else {throw Web3Error.processingError(desc: "Failed to get result of expected type from the Ethereum node")}
142141
return res

Sources/web3swift/Tokens/ERC1400/Web3+ERC1400.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,6 @@ extension ERC1400: IERC777 {
686686
public func supportsInterface(interfaceID: String) async throws -> Bool {
687687
let contract = self.contract
688688
self.transaction.callOnBlock = .latest
689-
self.transaction.gasLimitPolicy = .manual(30000)
690689
let result = try await contract.createReadOperation("supportsInterface", parameters: [interfaceID] as [AnyObject], extraData: Data() )!.callContractMethod()
691690
guard let res = result["0"] as? Bool else {throw Web3Error.processingError(desc: "Failed to get result of expected type from the Ethereum node")}
692691
return res

Sources/web3swift/Tokens/ERC1410/Web3+ERC1410.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,6 @@ extension ERC1410: IERC777 {
452452
public func supportsInterface(interfaceID: String) async throws -> Bool {
453453
let contract = self.contract
454454
self.transaction.callOnBlock = .latest
455-
self.transaction.gasLimitPolicy = .manual(30000)
456455
let result = try await contract.createReadOperation("supportsInterface", parameters: [interfaceID] as [AnyObject], extraData: Data() )!.callContractMethod()
457456
guard let res = result["0"] as? Bool else {throw Web3Error.processingError(desc: "Failed to get result of expected type from the Ethereum node")}
458457
return res

Sources/web3swift/Tokens/ERC1633/Web3+ERC1633.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,6 @@ public class ERC1633: IERC1633, ERC20BaseProperties {
180180
public func supportsInterface(interfaceID: String) async throws -> Bool {
181181
let contract = self.contract
182182
self.transaction.callOnBlock = .latest
183-
self.transaction.gasLimitPolicy = .manual(30000)
184183
let result = try await contract.createReadOperation("supportsInterface", parameters: [interfaceID] as [AnyObject], extraData: Data() )!.callContractMethod()
185184
guard let res = result["0"] as? Bool else {throw Web3Error.processingError(desc: "Failed to get result of expected type from the Ethereum node")}
186185
return res

0 commit comments

Comments
 (0)