Skip to content

Commit b500e24

Browse files
🔄 synced file(s) with circlefin/modularwallets-ios-sdk-internal (#19)
synced local file(s) with [circlefin/modularwallets-ios-sdk-internal](https://github.com/circlefin/modularwallets-ios-sdk-internal). <details> <summary>Changed files</summary> <ul> <li>synced local directory <code>CircleModularWalletsCore/</code> with remote directory <code>CircleModularWalletsCore/</code></li> </ul> </details> --- This PR was created automatically by the [repo-file-sync-action](https://github.com/BetaHuhn/repo-file-sync-action) workflow run [#15463160712](https://github.com/circlefin/modularwallets-ios-sdk-internal/actions/runs/15463160712)
1 parent 7e1d361 commit b500e24

File tree

10 files changed

+154
-41
lines changed

10 files changed

+154
-41
lines changed

‎CircleModularWalletsCore/Resources/Info.plist‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<plist version="1.0">
44
<dict>
55
<key>CFBundleShortVersionString</key>
6-
<string>1.1.2</string>
6+
<string>1.1.3</string>
77
<key>CFBundleIdentifier</key>
88
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
99
<key>CFBundleName</key>

‎CircleModularWalletsCore/Sources/APIs/Bundler/BundlerRpcReqResp.swift‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ public struct EstimateUserOperationGasResult: Codable {
3838
/// The amount of gas to allocate for the paymaster post-operation code.
3939
public let paymasterPostOpGasLimit: BigInt?
4040

41-
init(preVerificationGas: BigInt?, verificationGasLimit: BigInt?, callGasLimit: BigInt?, paymasterVerificationGasLimit: BigInt?, paymasterPostOpGasLimit: BigInt?) {
41+
init(preVerificationGas: BigInt?,
42+
verificationGasLimit: BigInt?,
43+
callGasLimit: BigInt?,
44+
paymasterVerificationGasLimit: BigInt?,
45+
paymasterPostOpGasLimit: BigInt?) {
4246
self.preVerificationGas = preVerificationGas
4347
self.verificationGasLimit = verificationGasLimit
4448
self.callGasLimit = callGasLimit

‎CircleModularWalletsCore/Sources/APIs/Modular/ModularRpcApi.swift‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@ import Foundation
2121
protocol ModularRpcApi {
2222

2323
func getAddress(transport: Transport, req: GetAddressReq) async throws -> ModularWallet
24+
2425
func createAddressMapping(
2526
transport: Transport,
2627
walletAddress: String,
2728
owners: [AddressMappingOwner]
2829
) async throws -> [CreateAddressMappingResult]
30+
31+
func getUserOperationGasPrice(
32+
transport: Transport
33+
) async throws -> GetUserOperationGasPriceResult
2934
}
3035

3136
extension ModularRpcApi {
@@ -74,4 +79,13 @@ extension ModularRpcApi {
7479
let response = try await transport.request(req) as RpcResponse<[CreateAddressMappingResult]>
7580
return response.result
7681
}
82+
83+
func getUserOperationGasPrice(
84+
transport: Transport
85+
) async throws -> GetUserOperationGasPriceResult {
86+
let params = [AnyEncodable]()
87+
let req = RpcRequest(method: "circle_getUserOperationGasPrice", params: params)
88+
let response = try await transport.request(req) as RpcResponse<GetUserOperationGasPriceResult>
89+
return response.result
90+
}
7791
}

‎CircleModularWalletsCore/Sources/APIs/Modular/ModularRpcReqResp.swift‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
//
1818

1919
import Foundation
20+
import BigInt
2021

2122
struct GetAddressReq: Encodable {
2223
let scaConfiguration: ScaConfiguration
@@ -102,3 +103,75 @@ struct CreateAddressMappingReq: Encodable {
102103
let walletAddress: String
103104
let owners: [AddressMappingOwner]
104105
}
106+
107+
/// Represents the response from the circle_getUserOperationGasPrice RPC method.
108+
/// This structure provides different gas price options (low, medium, high) for
109+
/// user operations along with verification gas limits for both
110+
/// deployed and non-deployed smart accounts.
111+
public struct GetUserOperationGasPriceResult: Decodable {
112+
113+
/// The low-priority, medium-priority and high-priority gas price option.
114+
public let low, medium, high: GasPriceOption
115+
116+
/// The optional deployed verification gas.
117+
public let deployed: BigInt?
118+
119+
/// The optional non-deployed verification gas.
120+
public let notDeployed: BigInt?
121+
122+
enum CodingKeys: CodingKey {
123+
case low, medium, high
124+
case verificationGasLimit
125+
case deployed
126+
case notDeployed
127+
}
128+
129+
init(low: GasPriceOption,
130+
medium: GasPriceOption,
131+
high: GasPriceOption,
132+
deployed: BigInt? = nil,
133+
notDeployed: BigInt? = nil) {
134+
self.low = low
135+
self.medium = medium
136+
self.high = high
137+
self.deployed = deployed
138+
self.notDeployed = notDeployed
139+
}
140+
141+
public init(from decoder: Decoder) throws {
142+
let container = try decoder.container(keyedBy: CodingKeys.self)
143+
self.low = try container.decode(GasPriceOption.self, forKey: .low)
144+
self.medium = try container.decode(GasPriceOption.self, forKey: .medium)
145+
self.high = try container.decode(GasPriceOption.self, forKey: .high)
146+
self.deployed = try container.decodeToBigInt(forKey: .deployed, isHex: false)
147+
self.notDeployed = try container.decodeToBigInt(forKey: .notDeployed, isHex: false)
148+
}
149+
}
150+
151+
/// Represents a gas price option.
152+
/// Contains the maximum fee per gas and maximum priority fee per gas for
153+
/// a specific priority level (low, medium, or high).
154+
public struct GasPriceOption: Decodable {
155+
156+
/// The maximum fee per gas.
157+
public let maxFeePerGas: BigInt
158+
159+
/// The maximum priority fee per gas.
160+
public let maxPriorityFeePerGas: BigInt
161+
162+
enum CodingKeys: CodingKey {
163+
case maxFeePerGas
164+
case maxPriorityFeePerGas
165+
}
166+
167+
init(maxFeePerGas: BigInt, maxPriorityFeePerGas: BigInt) {
168+
self.maxFeePerGas = maxFeePerGas
169+
self.maxPriorityFeePerGas = maxPriorityFeePerGas
170+
}
171+
172+
public init(from decoder: Decoder) throws {
173+
let container = try decoder.container(keyedBy: CodingKeys.self)
174+
self.maxFeePerGas = try container.decodeToBigInt(forKey: .maxFeePerGas, isHex: false) ?? .zero
175+
self.maxPriorityFeePerGas = try container.decodeToBigInt(forKey: .maxPriorityFeePerGas, isHex: false) ?? .zero
176+
}
177+
}

‎CircleModularWalletsCore/Sources/Accounts/CircleSmartAccount.swift‎

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,15 @@ public class CircleSmartAccount<A: Account>: SmartAccount, @unchecked Sendable w
6565
}
6666

6767
convenience init(client: Client, owner: A, version: String, name: String?) async throws {
68-
guard let buidlTransport = client.transport as? ModularTransport else {
68+
guard let bundlerTransport = client.transport as? ModularTransport else {
6969
throw BaseError(shortMessage: "The property client.transport is not the ModularTransport")
7070
}
7171
guard let webAuthnAccount = owner as? WebAuthnAccount else {
7272
throw BaseError(shortMessage: "The property owner is not the WebAuthnAccount")
7373
}
7474

7575
let wallet = try await Self.createWallet(
76-
transport: buidlTransport,
76+
transport: bundlerTransport,
7777
hexPublicKey: webAuthnAccount.credential.publicKey,
7878
version: version,
7979
name: name
@@ -85,20 +85,21 @@ public class CircleSmartAccount<A: Account>: SmartAccount, @unchecked Sendable w
8585
/// Configuration for the user operation.
8686
public var userOperation: UserOperationConfiguration? {
8787
get async {
88-
let minimumVerificationGasLimit = SmartAccountUtils.getMinimumVerificationGasLimit(
89-
deployed: await self.isDeployed(),
90-
chainId: client.chain.chainId
91-
)
92-
9388
let config = UserOperationConfiguration { userOperation in
94-
let verificationGasLimit = BigInt(minimumVerificationGasLimit)
95-
let maxGasLimit = max(verificationGasLimit, userOperation.verificationGasLimit ?? BigInt(0))
96-
97-
return EstimateUserOperationGasResult(preVerificationGas: nil,
98-
verificationGasLimit: maxGasLimit,
99-
callGasLimit: nil,
100-
paymasterVerificationGasLimit: nil,
101-
paymasterPostOpGasLimit: nil)
89+
// Only call getDefaultVerificationGasLimit if verificationGasLimit is not provided
90+
let verificationGasLimit = userOperation.verificationGasLimit != nil ?
91+
userOperation.verificationGasLimit : await SmartAccountUtils.getDefaultVerificationGasLimit(
92+
client: self.client,
93+
deployed: await self.isDeployed()
94+
)
95+
96+
return EstimateUserOperationGasResult(
97+
preVerificationGas: nil,
98+
verificationGasLimit: verificationGasLimit,
99+
callGasLimit: nil,
100+
paymasterVerificationGasLimit: nil,
101+
paymasterPostOpGasLimit: nil
102+
)
102103
}
103104

104105
return config

‎CircleModularWalletsCore/Sources/Clients/BundlerClient.swift‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,14 +274,27 @@ public class BundlerClient: Client, BundlerRpcApi, PublicRpcApi {
274274
walletAddress: String,
275275
owners: [AddressMappingOwner]
276276
) async throws -> [CreateAddressMappingResult] {
277-
guard let buidlTransport = transport as? ModularTransport else {
277+
guard let bundlerTransport = transport as? ModularTransport else {
278278
throw BaseError(shortMessage: "The property transport is not the ModularTransport")
279279
}
280280

281-
return try await buidlTransport.createAddressMapping(
282-
transport: buidlTransport,
281+
return try await bundlerTransport.createAddressMapping(
282+
transport: bundlerTransport,
283283
walletAddress: walletAddress,
284284
owners: owners
285285
)
286286
}
287+
288+
/// Gets the gas price options for a user operation with optional SDK version parameter.
289+
///
290+
/// - Returns: The gas price options with low, medium, high tiers and optional verificationGasLimit.
291+
public func getUserOperationGasPrice() async throws -> GetUserOperationGasPriceResult {
292+
guard let bundlerTransport = transport as? ModularTransport else {
293+
throw BaseError(shortMessage: "The property transport is not the ModularTransport")
294+
}
295+
296+
return try await bundlerTransport.getUserOperationGasPrice(
297+
transport: bundlerTransport
298+
)
299+
}
287300
}

‎CircleModularWalletsCore/Sources/Helpers/Constants.swift‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,3 @@ let THRESHOLD_WEIGHT = 1
7070

7171
let MINIMUM_VERIFICATION_GAS_LIMIT = 100_000
7272
let MINIMUM_UNDEPLOY_VERIFICATION_GAS_LIMIT = 1_500_000
73-
let SEPOLIA_MINIMUM_VERIFICATION_GAS_LIMIT = 600_000
74-
let SEPOLIA_MINIMUM_UNDEPLOY_VERIFICATION_GAS_LIMIT = 2_000_000
75-
let MAINNET_MINIMUM_VERIFICATION_GAS_LIMIT = 1_000_000
76-
let MAINNET_MINIMUM_UNDEPLOY_VERIFICATION_GAS_LIMIT = 2_500_000

‎CircleModularWalletsCore/Sources/Helpers/Extensions/Bundle+Extension.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import Foundation
2121
#if SWIFT_PACKAGE
2222
extension Bundle {
2323
public enum SDK {
24-
public static let version = "1.1.2"
24+
public static let version = "1.1.3"
2525
}
2626
}
2727
#else

‎CircleModularWalletsCore/Sources/Helpers/Extensions/KeyedDecodingContainer+Extension.swift‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import BigInt
2121

2222
extension KeyedDecodingContainer {
2323

24-
func decodeToBigInt(forKey key: KeyedDecodingContainer<K>.Key) throws -> BigInt? {
25-
let hexString = try? self.decodeIfPresent(String.self, forKey: key)
26-
return HexUtils.hexToBigInt(hex: hexString)
24+
func decodeToBigInt(forKey key: KeyedDecodingContainer<K>.Key,
25+
isHex: Bool = true) throws -> BigInt? {
26+
let string = try? self.decodeIfPresent(String.self, forKey: key)
27+
return isHex ?
28+
HexUtils.hexToBigInt(hex: string) : (string != nil ? BigInt(string ?? "") : nil)
2729
}
2830

2931
// func decodeBytesFromURLEncodedBase64(forKey key: KeyedDecodingContainer.Key) throws -> [UInt8] {

‎CircleModularWalletsCore/Sources/Helpers/Utils/SmartAccountUtils.swift‎

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,33 @@
1717
//
1818

1919
import Foundation
20+
import BigInt
2021

2122
struct SmartAccountUtils {
2223

23-
static func getMinimumVerificationGasLimit(deployed: Bool,
24-
chainId: Int) -> Int {
25-
switch chainId {
26-
case Sepolia.chainId:
27-
return deployed ?
28-
SEPOLIA_MINIMUM_VERIFICATION_GAS_LIMIT : SEPOLIA_MINIMUM_UNDEPLOY_VERIFICATION_GAS_LIMIT
29-
30-
case Mainnet.chainId:
31-
return deployed ?
32-
MAINNET_MINIMUM_VERIFICATION_GAS_LIMIT : MAINNET_MINIMUM_UNDEPLOY_VERIFICATION_GAS_LIMIT
33-
default:
34-
return deployed ?
35-
MINIMUM_VERIFICATION_GAS_LIMIT : MINIMUM_UNDEPLOY_VERIFICATION_GAS_LIMIT
24+
static func getDefaultVerificationGasLimit(client: Client,
25+
deployed: Bool) async -> BigInt? {
26+
var verificationGasLimit: BigInt = deployed ?
27+
BigInt(MINIMUM_VERIFICATION_GAS_LIMIT) : BigInt(MINIMUM_UNDEPLOY_VERIFICATION_GAS_LIMIT)
28+
29+
guard let bundlerClient = client as? BundlerClient,
30+
client.transport is ModularTransport else {
31+
logger.transport.error("Client is not BundlerClient / Client transport is not ModularTransport")
32+
return nil
33+
}
34+
35+
guard let result = try? await bundlerClient.getUserOperationGasPrice() else {
36+
logger.bundler.error("Failed to get gas prices from RPC, falling back to hardcoded values: \(verificationGasLimit)")
37+
return verificationGasLimit
3638
}
39+
40+
if deployed, let deployedVerificationGasLimit = result.deployed {
41+
verificationGasLimit = deployedVerificationGasLimit
42+
} else if !deployed, let notDeployedVerificationGasLimit = result.notDeployed {
43+
verificationGasLimit = notDeployedVerificationGasLimit
44+
}
45+
46+
return verificationGasLimit
3747
}
3848

3949
}

0 commit comments

Comments
 (0)