Skip to content

Commit bb1c8ed

Browse files
Support starknet_getBlockWithTxs (#242)
1 parent 84e5dd1 commit bb1c8ed

File tree

11 files changed

+260
-9
lines changed

11 files changed

+260
-9
lines changed

Package.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,9 @@ let package = Package(
4141
.process("Resources"),
4242
]
4343
),
44+
.testTarget(
45+
name: "NetworkTests",
46+
dependencies: ["Starknet"]
47+
),
4448
]
4549
)

Sources/Starknet/Data/Block.swift

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
public enum BlockStatus: String, Codable {
2+
case preConfirmed = "PRE_CONFIRMED"
3+
case acceptedOnL1 = "ACCEPTED_ON_L1"
4+
case acceptedOnL2 = "ACCEPTED_ON_L2"
5+
case rejected = "REJECTED"
6+
}
7+
8+
public protocol StarknetBlock: Codable {
9+
var timestamp: Int { get }
10+
var sequencerAddress: Felt { get }
11+
var blockNumber: Int { get }
12+
var l1GasPrice: StarknetResourcePrice { get }
13+
var l2GasPrice: StarknetResourcePrice { get }
14+
var l1DataGasPrice: StarknetResourcePrice { get }
15+
var l1DataAvailabilityMode: StarknetL1DAMode { get }
16+
var starknetVersion: String { get }
17+
}
18+
19+
public protocol StarknetProcessedBlock: StarknetBlock {
20+
var status: BlockStatus { get }
21+
var blockHash: Felt { get }
22+
var parentHash: Felt { get }
23+
var newRoot: Felt { get }
24+
}
25+
26+
public protocol StarknetPreConfirmedBlock: StarknetBlock {}
27+
28+
public protocol StarknetBlockWithTxs: StarknetBlock {
29+
var transactions: [TransactionWrapper] { get }
30+
}
31+
32+
public struct StarknetProcessedBlockWithTxs: StarknetProcessedBlock, StarknetBlockWithTxs, Encodable {
33+
public let status: BlockStatus
34+
public let transactions: [TransactionWrapper]
35+
public let blockHash: Felt
36+
public let parentHash: Felt
37+
public let blockNumber: Int
38+
public let newRoot: Felt
39+
public let timestamp: Int
40+
public let sequencerAddress: Felt
41+
public let l1GasPrice: StarknetResourcePrice
42+
public let l2GasPrice: StarknetResourcePrice
43+
public let l1DataGasPrice: StarknetResourcePrice
44+
public let l1DataAvailabilityMode: StarknetL1DAMode
45+
public let starknetVersion: String
46+
47+
enum CodingKeys: String, CodingKey {
48+
case status
49+
case transactions
50+
case blockHash = "block_hash"
51+
case parentHash = "parent_hash"
52+
case blockNumber = "block_number"
53+
case newRoot = "new_root"
54+
case timestamp
55+
case sequencerAddress = "sequencer_address"
56+
case l1GasPrice = "l1_gas_price"
57+
case l2GasPrice = "l2_gas_price"
58+
case l1DataGasPrice = "l1_data_gas_price"
59+
case l1DataAvailabilityMode = "l1_da_mode"
60+
case starknetVersion = "starknet_version"
61+
}
62+
}
63+
64+
public struct StarknetPreConfirmedBlockWithTxs: StarknetPreConfirmedBlock, StarknetBlockWithTxs, Codable {
65+
public let transactions: [TransactionWrapper]
66+
public let blockNumber: Int
67+
public let timestamp: Int
68+
public let sequencerAddress: Felt
69+
public let l1GasPrice: StarknetResourcePrice
70+
public let l2GasPrice: StarknetResourcePrice
71+
public let l1DataGasPrice: StarknetResourcePrice
72+
public let l1DataAvailabilityMode: StarknetL1DAMode
73+
public let starknetVersion: String
74+
75+
enum CodingKeys: String, CodingKey {
76+
case transactions
77+
case blockNumber = "block_number"
78+
case timestamp
79+
case sequencerAddress = "sequencer_address"
80+
case l1GasPrice = "l1_gas_price"
81+
case l2GasPrice = "l2_gas_price"
82+
case l1DataGasPrice = "l1_data_gas_price"
83+
case l1DataAvailabilityMode = "l1_da_mode"
84+
case starknetVersion = "starknet_version"
85+
}
86+
}
87+
88+
public enum StarknetBlockWithTxsWrapper: Codable {
89+
case processed(StarknetProcessedBlockWithTxs)
90+
case preConfirmed(StarknetPreConfirmedBlockWithTxs)
91+
92+
public init(from decoder: Decoder) throws {
93+
let container = try decoder.container(keyedBy: CodingKeys.self)
94+
if container.contains(.parentHash) {
95+
let block = try StarknetProcessedBlockWithTxs(from: decoder)
96+
self = .processed(block)
97+
} else {
98+
let block = try StarknetPreConfirmedBlockWithTxs(from: decoder)
99+
self = .preConfirmed(block)
100+
}
101+
}
102+
103+
public func encode(to encoder: Encoder) throws {
104+
switch self {
105+
case let .processed(block): try block.encode(to: encoder)
106+
case let .preConfirmed(block): try block.encode(to: encoder)
107+
}
108+
}
109+
110+
private enum CodingKeys: String, CodingKey {
111+
case parentHash = "parent_hash"
112+
}
113+
}

Sources/Starknet/Data/StarknetBlockId.swift

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
import Foundation
22

33
public enum StarknetBlockId: Equatable {
4-
public enum BlockTag: String {
4+
public enum BlockTag: String, Codable {
55
case latest
6-
case preConfirmed
6+
case preConfirmed = "pre_confirmed"
77
}
88

99
case hash(Felt)
1010
case number(Int)
1111
case tag(BlockTag)
12-
13-
enum CodingKeys: String, CodingKey {
14-
case latest
15-
case preConfirmed = "pre_confirmed"
16-
}
1712
}
1813

1914
extension StarknetBlockId: Encodable {
@@ -30,7 +25,7 @@ extension StarknetBlockId: Encodable {
3025
]
3126
try dict.encode(to: encoder)
3227
case let .tag(blockTag):
33-
try blockTag.rawValue.encode(to: encoder)
28+
try blockTag.encode(to: encoder)
3429
}
3530
}
3631
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public enum StarknetL1DAMode: String, Codable {
2+
case blob = "BLOB"
3+
case calldata = "CALLDATA"
4+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
public struct StarknetResourcePrice: Codable, Equatable {
2+
public let priceInWei: Felt
3+
public let priceInFri: Felt
4+
5+
enum CodingKeys: String, CodingKey {
6+
case priceInWei = "price_in_wei"
7+
case priceInFri = "price_in_fri"
8+
}
9+
}

Sources/Starknet/Data/Transaction/TransactionWrapper.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Foundation
22

33
/// Transaction wrapper used for decoding polymorphic StarknetTransaction
4-
public enum TransactionWrapper: Decodable {
4+
public enum TransactionWrapper: Decodable, Encodable {
55
fileprivate enum Keys: String, CodingKey {
66
case type
77
case version

Sources/Starknet/Network/StarknetRequest.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,4 +347,46 @@ public enum RequestBuilder {
347347
public static func simulateTransactions(_ transactions: [any StarknetExecutableTransaction], simulationFlags: Set<StarknetSimulationFlag>) -> StarknetRequest<[StarknetSimulatedTransaction]> {
348348
simulateTransactions(transactions, at: defaultBlockId, simulationFlags: simulationFlags)
349349
}
350+
351+
/// Get a block with transactions.
352+
///
353+
/// - Parameters:
354+
/// - blockId: hash, number, or tag of the requested block.
355+
///
356+
/// - Returns: Block information with full transactions.
357+
public static func getBlockWithTxs(_ blockId: StarknetBlockId) -> StarknetRequest<StarknetBlockWithTxsWrapper> {
358+
let params = GetBlockWithTxsParams(blockId: blockId)
359+
360+
return StarknetRequest(method: .getBlockWithTxs, params: .getBlockWithTxs(params))
361+
}
362+
363+
/// Get a block with transactions by block hash.
364+
///
365+
/// - Parameters:
366+
/// - blockHash: hash of the requested block.
367+
///
368+
/// - Returns: Block information with full transactions.
369+
public static func getBlockWithTxs(_ blockHash: Felt) -> StarknetRequest<StarknetBlockWithTxsWrapper> {
370+
getBlockWithTxs(StarknetBlockId.hash(blockHash))
371+
}
372+
373+
/// Get a block with transactions by block number.
374+
///
375+
/// - Parameters:
376+
/// - blockNumber: number of the requested block.
377+
///
378+
/// - Returns: Block information with full transactions.
379+
public static func getBlockWithTxs(_ blockNumber: Int) -> StarknetRequest<StarknetBlockWithTxsWrapper> {
380+
getBlockWithTxs(StarknetBlockId.number(blockNumber))
381+
}
382+
383+
/// Get a block with transactions by block tag.
384+
///
385+
/// - Parameters:
386+
/// - blockTag: tag of the requested block.
387+
///
388+
/// - Returns: Block information with full transactions.
389+
public static func getBlockWithTxs(_ blockTag: StarknetBlockId.BlockTag) -> StarknetRequest<StarknetBlockWithTxsWrapper> {
390+
getBlockWithTxs(StarknetBlockId.tag(blockTag))
391+
}
350392
}

Sources/Starknet/Providers/StarknetProvider/JsonRpcMethod.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ enum JsonRpcMethod: String, Encodable {
1010
case getClassHashAt = "starknet_getClassHashAt"
1111
case getBlockNumber = "starknet_blockNumber"
1212
case getBlockHashAndNumber = "starknet_blockHashAndNumber"
13+
case getBlockWithTxs = "starknet_getBlockWithTxs"
1314
case getEvents = "starknet_getEvents"
1415
case getStorageProof = "starknet_getStorageProof"
1516
case getTransactionByHash = "starknet_getTransactionByHash"

Sources/Starknet/Providers/StarknetProvider/JsonRpcParams.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,14 @@ struct SimulateTransactionsParams: Encodable {
185185
}
186186
}
187187

188+
struct GetBlockWithTxsParams: Encodable {
189+
let blockId: StarknetBlockId
190+
191+
enum CodingKeys: String, CodingKey {
192+
case blockId = "block_id"
193+
}
194+
}
195+
188196
enum JsonRpcParams {
189197
case getNonce(GetNonceParams)
190198
case addInvokeTransaction(AddInvokeTransactionParams)
@@ -194,6 +202,7 @@ enum JsonRpcParams {
194202
case estimateFee(EstimateFeeParams)
195203
case estimateMessageFee(EstimateMessageFeeParams)
196204
case addDeployAccountTransaction(AddDeployAccountTransactionParams)
205+
case getBlockWithTxs(GetBlockWithTxsParams)
197206
case getClassHashAt(GetClassHashAtParams)
198207
case getEvents(GetEventsPayload)
199208
case getStorageProof(GetStorageProofParams)
@@ -224,6 +233,8 @@ extension JsonRpcParams: Encodable {
224233
try params.encode(to: encoder)
225234
case let .addDeployAccountTransaction(params):
226235
try params.encode(to: encoder)
236+
case let .getBlockWithTxs(params):
237+
try params.encode(to: encoder)
227238
case let .getClassHashAt(params):
228239
try params.encode(to: encoder)
229240
case let .getEvents(params):
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import XCTest
2+
3+
@testable import Starknet
4+
5+
final class ProviderTests: XCTestCase {
6+
var provider: StarknetProviderProtocol!
7+
8+
override class func setUp() {
9+
super.setUp()
10+
}
11+
12+
override func setUp() async throws {
13+
try await super.setUp()
14+
15+
// TODO(#245): Change this to internal node
16+
provider = StarknetProvider(url: "https://rpc.pathfinder.equilibrium.co/testnet-sepolia/rpc/v0_9")
17+
}
18+
19+
func testGetBlockWithTxsWithLatestBlockTag() async throws {
20+
let result = try await provider.send(request: RequestBuilder.getBlockWithTxs(StarknetBlockId.BlockTag.latest))
21+
22+
if case .preConfirmed = result {
23+
XCTFail("Expected .processed")
24+
}
25+
}
26+
27+
func testGetBlockWithTxsWithPreConfirmedBlockTag() async throws {
28+
let result = try await provider.send(request: RequestBuilder.getBlockWithTxs(StarknetBlockId.BlockTag.preConfirmed))
29+
30+
if case .processed = result {
31+
XCTFail("Expected .preConfirmed")
32+
}
33+
}
34+
35+
func testGetBlockWithTxsWithBlockHash() async throws {
36+
let blockHash = Felt(fromHex: "0x05d95c778dad488e15f6a279c77c59322ad61eabf085cd8624ff5b39ca5ae8d8")!
37+
let result = try await provider.send(request: RequestBuilder.getBlockWithTxs(blockHash))
38+
39+
if case let .processed(processedBlock) = result {
40+
XCTAssertEqual(processedBlock.transactions.count, 7)
41+
} else {
42+
XCTFail("Expected .processed")
43+
}
44+
}
45+
46+
func testGetBlockWithTxsWithBlockNumber() async throws {
47+
let blockNumber = 1_210_000
48+
let result = try await provider.send(request: RequestBuilder.getBlockWithTxs(blockNumber))
49+
50+
if case let .processed(processedBlock) = result {
51+
XCTAssertEqual(processedBlock.transactions.count, 8)
52+
} else {
53+
XCTFail("Expected .processed")
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)