Skip to content
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
1bd2629
Reflect spec changes
franciszekjob Jul 15, 2025
d409c41
Add tip support
franciszekjob Jul 15, 2025
95dc6bb
Simplify constructors for fee estimate structs
franciszekjob Jul 15, 2025
45bf10f
Fix `testSimulateTransactionsV3`
franciszekjob Jul 15, 2025
7f74704
Fix newline
franciszekjob Jul 15, 2025
74a2b09
Merge branch 'feat/rpc-0.9.0' of https://github.com/software-mansion/…
franciszekjob Jul 15, 2025
3879956
Fix tests
franciszekjob Jul 15, 2025
44dbd3b
Fix tip value in constructor
franciszekjob Jul 15, 2025
d4d91b9
Remove unused variables
franciszekjob Jul 15, 2025
b45ae20
Restore `ethErc20ContractAddress`
franciszekjob Jul 15, 2025
64a4d62
Apply code review suggestions
franciszekjob Jul 17, 2025
66d81f9
Add comment
franciszekjob Jul 17, 2025
496f776
Merge branch 'main' of https://github.com/software-mansion/starknet.s…
franciszekjob Jul 17, 2025
bd822ea
Merge branch 'feat/rpc-0.9.0' of https://github.com/software-mansion/…
franciszekjob Jul 17, 2025
ba6c958
Merge branch 'main' of https://github.com/software-mansion/starknet.s…
franciszekjob Jul 17, 2025
281bd7c
Support `starknet_getBlockWithTxs`
franciszekjob Jul 28, 2025
529a31f
Merge branch 'main' into get-block-with-txs
franciszekjob Jul 28, 2025
ebf9ecf
Formatting
franciszekjob Jul 28, 2025
5bbe0d6
Merge branch 'get-block-with-txs' of https://github.com/software-mans…
franciszekjob Jul 28, 2025
30e281b
Add network tests
franciszekjob Jul 28, 2025
db95e96
Fix commas
franciszekjob Jul 28, 2025
1eaf18b
Support tip estimation
franciszekjob Jul 28, 2025
7c9ff4b
Remove comma
franciszekjob Jul 28, 2025
d0f4fd8
Little refactor of mapping txs
franciszekjob Jul 28, 2025
1e5ad3b
Add comma
franciszekjob Jul 28, 2025
65974a4
Merge branch 'get-block-with-txs' into feat/239-tip-auto-estimation
franciszekjob Jul 28, 2025
042c85b
Remove tests for getting block from regular test suite
franciszekjob Jul 29, 2025
78a0f72
Add todo
franciszekjob Jul 29, 2025
9047264
Merge branch 'get-block-with-txs' into feat/239-tip-auto-estimation
franciszekjob Jul 29, 2025
61805c0
Add pre-confirmed coding key in `BlockTag`
franciszekjob Jul 29, 2025
d3435ba
Merge branch 'get-block-with-txs' into feat/239-tip-auto-estimation
franciszekjob Jul 29, 2025
cca46f5
Add test case for l1 accepted block tag
franciszekjob Jul 30, 2025
3493b63
Remove `BlockStatus.REJECTED`
franciszekjob Jul 30, 2025
ba32a39
Merge branch 'feat/239-tip-auto-estimation' of https://github.com/sof…
franciszekjob Jul 30, 2025
5a22432
Use `guard`
franciszekjob Jul 31, 2025
040a812
Fix test for `.preConfirmed` case
franciszekjob Jul 31, 2025
1719a66
Merge branch 'main' of https://github.com/software-mansion/starknet.s…
franciszekjob Jul 31, 2025
798ccd3
Merge branch 'feat/239-tip-auto-estimation' into feat/rpc-0.9.0-rc.2
franciszekjob Jul 31, 2025
70e4b31
Merge branch 'main' into feat/rpc-0.9.0-rc.2
franciszekjob Jul 31, 2025
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
1 change: 0 additions & 1 deletion Sources/Starknet/Data/Block.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ public enum BlockStatus: String, Codable {
case preConfirmed = "PRE_CONFIRMED"
case acceptedOnL1 = "ACCEPTED_ON_L1"
case acceptedOnL2 = "ACCEPTED_ON_L2"
case rejected = "REJECTED"
}

public protocol StarknetBlock: Codable {
Expand Down
1 change: 1 addition & 0 deletions Sources/Starknet/Data/StarknetBlockId.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Foundation

public enum StarknetBlockId: Equatable {
public enum BlockTag: String, Codable {
case l1Accepted = "l1_accepted"
case latest
case preConfirmed = "pre_confirmed"
}
Expand Down
89 changes: 89 additions & 0 deletions Sources/Starknet/Helpers/Tip.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import BigInt
import Foundation

/// Estimates the transaction tip by taking the median of all V3 transaction tips in the latest block.
///
/// - Parameter provider: The provider used to interact with Starknet.
/// - Returns: The estimated median tip.
/// - Throws: An error if the RPC call fails or no transactions are found.
public func estimateTip(provider: StarknetProviderProtocol) async throws -> UInt64AsHex {
try await estimateTip(provider: provider, blockId: .tag(.latest))
}

/// Estimates the transaction tip by taking the median of all V3 transaction tips in the latest block.
///
/// - Parameters:
/// - provider: The provider used to interact with Starknet.
/// - blockHash: The block hash to estimate tip for.
/// - Returns: The estimated median tip.
/// - Throws: An error if the RPC call fails or no transactions are found.
public func estimateTip(provider: StarknetProviderProtocol, blockHash: Felt) async throws -> UInt64AsHex {
try await estimateTip(provider: provider, blockId: .hash(blockHash))
}

/// Estimates the transaction tip by taking the median of all V3 transaction tips in the latest block.
///
/// - Parameters:
/// - provider: The provider used to interact with Starknet.
/// - blockNumber: The block number to estimate tip for.
/// - Returns: The estimated median tip.
/// - Throws: An error if the RPC call fails or no transactions are found.
public func estimateTip(provider: StarknetProviderProtocol, blockNumber: Int) async throws -> UInt64AsHex {
try await estimateTip(provider: provider, blockId: .number(blockNumber))
}

/// Estimates the transaction tip by taking the median of all V3 transaction tips in the latest block.
///
/// - Parameters:
/// - provider: The provider used to interact with Starknet.
/// - blockTag: The block tag to estimate tip for.
/// - Returns: The estimated median tip.
/// - Throws: An error if the RPC call fails or no transactions are found.
public func estimateTip(provider: StarknetProviderProtocol, blockTag: StarknetBlockId.BlockTag) async throws -> UInt64AsHex {
try await estimateTip(provider: provider, blockId: .tag(blockTag))
}

/// Estimates the transaction tip by taking the median of all V3 transaction tips in the specified block.
///
/// - Parameters:
/// - provider: The provider used to interact with Starknet.
/// - blockId: The block identifier to estimate the tip for (hash, number, or tag).
/// - Returns: The estimated median tip.
/// - Throws: An error if the RPC call fails or no transactions are found.
private func estimateTip(provider: StarknetProviderProtocol, blockId: StarknetBlockId) async throws -> UInt64AsHex {
let request = RequestBuilder.getBlockWithTxs(blockId)
let blockWithTxs = try await provider.send(request: request)

let transactions: [TransactionWrapper] = switch blockWithTxs {
case let .processed(block): block.transactions
case let .preConfirmed(block): block.transactions
}

let tips = transactions.compactMap { transactionWrapper in
switch transactionWrapper {
case let .invokeV3(invokeV3): invokeV3.tip.value
case let .deployAccountV3(deployAccountV3): deployAccountV3.tip.value
case let .declareV3(declareV3): declareV3.tip.value
default: nil
}
}

if tips.isEmpty {
return UInt64AsHex.zero
}

let sortedTips = tips.sorted()
let count = sortedTips.count

let median = if count % 2 == 1 {
sortedTips[count / 2]
} else {
(sortedTips[count / 2 - 1] + sortedTips[count / 2]) / 2
}

if let median = median.toUInt64AsHex() {
return median
} else {
fatalError("Failed to convert BigUInt to UInt64AsHex")
}
}
47 changes: 47 additions & 0 deletions Sources/Starknet/Network/MockURLProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Foundation

final class MockURLProtocol: URLProtocol {
static var mockResponse: (statusCode: Int, body: Data)?

override class func canInit(with _: URLRequest) -> Bool {
true
}

override class func canonicalRequest(for request: URLRequest) -> URLRequest {
request
}

override func startLoading() {
guard let mock = MockURLProtocol.mockResponse else {
fatalError("Mock response not set")
}

let response = HTTPURLResponse(
url: request.url!,
statusCode: mock.statusCode,
httpVersion: nil,
headerFields: nil
)!

client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
client?.urlProtocol(self, didLoad: mock.body)
client?.urlProtocolDidFinishLoading(self)
}

override func stopLoading() {}
}

func makeMockedURLSession(
statusCode: Int = 200,
data: Data
) -> URLSession {
MockURLProtocol.mockResponse = (
statusCode: statusCode,
body: data
)

let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [MockURLProtocol.self]

return URLSession(configuration: config)
}
19 changes: 15 additions & 4 deletions Tests/NetworkTests/Providers/ProviderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,30 @@ final class ProviderTests: XCTestCase {
provider = StarknetProvider(url: "https://rpc.pathfinder.equilibrium.co/testnet-sepolia/rpc/v0_9")
}

func testGetBlockWithTxsWithL1AcceptedBlockTag() async throws {
let result = try await provider.send(request: RequestBuilder.getBlockWithTxs(StarknetBlockId.BlockTag.l1Accepted))

guard case .processed = result else {
XCTFail("Expected result to be of type .processed")
return
}
}

func testGetBlockWithTxsWithLatestBlockTag() async throws {
let result = try await provider.send(request: RequestBuilder.getBlockWithTxs(StarknetBlockId.BlockTag.latest))

if case .preConfirmed = result {
XCTFail("Expected .processed")
guard case .processed = result else {
XCTFail("Expected result to be of type .processed")
return
}
}

func testGetBlockWithTxsWithPreConfirmedBlockTag() async throws {
let result = try await provider.send(request: RequestBuilder.getBlockWithTxs(StarknetBlockId.BlockTag.preConfirmed))

if case .processed = result {
XCTFail("Expected .preConfirmed")
guard case .preConfirmed = result else {
XCTFail("Expected result to be of type .preConfirmed")
return
}
}

Expand Down
Loading
Loading