Skip to content

Commit 4b6ff14

Browse files
authored
Merge pull request #143 from onflow/get-system-transaction-endpoints
Add GetSystemTransaction and GetSystemTransactionResult endpoints
2 parents a8a17a4 + e697552 commit 4b6ff14

File tree

16 files changed

+669
-587
lines changed

16 files changed

+669
-587
lines changed

java-example/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ Below is a list of all Java code examples currently supported in this repo:
8686
[Get transactions.](src/main/java/org/onflow/examples/java/getTransaction/GetTransactionAccessAPIConnector.java)
8787

8888
- Get transaction
89+
- Get system transaction
8990
- Get transaction result
91+
- Get system transaction result
9092
- Get transaction result by index
9193

9294
#### Sending Transactions

java-example/src/main/java/org/onflow/examples/java/getTransaction/GetTransactionAccessAPIConnector.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ public FlowTransaction getTransaction(FlowId txID) {
2222
}
2323
}
2424

25+
public FlowTransaction getSystemTransaction(FlowId blockId) {
26+
FlowAccessApi.AccessApiCallResponse<FlowTransaction> response = accessAPI.getSystemTransaction(blockId);
27+
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
28+
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowTransaction>) response).getData();
29+
} else {
30+
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
31+
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
32+
}
33+
}
34+
2535
public FlowTransactionResult getTransactionResult(FlowId txID) {
2636
FlowAccessApi.AccessApiCallResponse<FlowTransactionResult> response = accessAPI.getTransactionResultById(txID);
2737
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
@@ -32,6 +42,16 @@ public FlowTransactionResult getTransactionResult(FlowId txID) {
3242
}
3343
}
3444

45+
public FlowTransactionResult getSystemTransactionResult(FlowId blockId) {
46+
FlowAccessApi.AccessApiCallResponse<FlowTransactionResult> response = accessAPI.getSystemTransactionResult(blockId);
47+
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
48+
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowTransactionResult>) response).getData();
49+
} else {
50+
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
51+
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
52+
}
53+
}
54+
3555
public FlowTransactionResult getTransactionResultByIndex(FlowId blockId, Integer index) {
3656
FlowAccessApi.AccessApiCallResponse<FlowTransactionResult> response = accessAPI.getTransactionResultByIndex(blockId, index);
3757
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {

kotlin-example/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ Below is a list of all Kotlin code examples currently supported in this repo:
8787
[Get transactions.](src/main/kotlin/org/onflow/examples/kotlin/getTransaction/GetTransactionAccessAPIConnector.kt)
8888

8989
- Get transaction
90+
- Get system transaction
9091
- Get transaction result
92+
- Get system transaction result
9193
- Get transaction result by index
9294

9395
#### Sending Transactions

kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getTransaction/GetTransactionAccessAPIConnector.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,24 @@ class GetTransactionAccessAPIConnector(
1111
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
1212
}
1313

14+
fun getSystemTransaction(blockId: FlowId): FlowTransaction =
15+
when (val response = accessAPI.getSystemTransaction(blockId)) {
16+
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
17+
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
18+
}
19+
1420
fun getTransactionResult(txID: FlowId): FlowTransactionResult =
1521
when (val response = accessAPI.getTransactionResultById(txID)) {
1622
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
1723
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
1824
}
1925

26+
fun getSystemTransactionResult(blockId: FlowId): FlowTransactionResult =
27+
when (val response = accessAPI.getSystemTransactionResult(blockId)) {
28+
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
29+
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
30+
}
31+
2032
fun getTransactionResultByIndex(blockId: FlowId, index: Int): FlowTransactionResult =
2133
when (val response = accessAPI.getTransactionResultByIndex(blockId, index)) {
2234
is FlowAccessApi.AccessApiCallResponse.Success -> response.data

kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getAccountBalance/GetAccountBalanceAccessAPIConnectorTest.kt

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ package org.onflow.examples.kotlin.getAccountBalance
33
import org.junit.jupiter.api.Assertions
44
import org.junit.jupiter.api.BeforeEach
55
import org.junit.jupiter.api.Test
6+
import org.onflow.examples.kotlin.getTransaction.GetTransactionAccessAPIConnectorTest
67
import org.onflow.flow.common.test.FlowEmulatorProjectTest
78
import org.onflow.flow.common.test.FlowServiceAccountCredentials
89
import org.onflow.flow.common.test.FlowTestClient
910
import org.onflow.flow.common.test.TestAccount
1011
import org.onflow.flow.sdk.FlowAccessApi
12+
import org.onflow.flow.sdk.FlowBlock
1113

1214
@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json")
1315
internal class GetAccountBalanceAccessAPIConnectorTest {
@@ -18,10 +20,12 @@ internal class GetAccountBalanceAccessAPIConnectorTest {
1820
lateinit var accessAPI: FlowAccessApi
1921

2022
private lateinit var balanceAPIConnector: GetAccountBalanceAccessAPIConnector
23+
private lateinit var latestBlock: FlowBlock
2124

2225
@BeforeEach
2326
fun setup() {
2427
balanceAPIConnector = GetAccountBalanceAccessAPIConnector(accessAPI)
28+
latestBlock = GetTransactionAccessAPIConnectorTest.fetchLatestBlockWithRetries(accessAPI)
2529
}
2630

2731
@Test
@@ -36,17 +40,10 @@ internal class GetAccountBalanceAccessAPIConnectorTest {
3640
@Test
3741
fun `Can fetch account balance at a specific block height`() {
3842
val address = serviceAccount.flowAddress
39-
val latestBlock = accessAPI.getLatestBlock(true) // Fetch the latest sealed block
43+
val balanceAtHeight = balanceAPIConnector.getBalanceAtBlockHeight(address, latestBlock.height)
4044

41-
when (latestBlock) {
42-
is FlowAccessApi.AccessApiCallResponse.Success -> {
43-
val balanceAtHeight = balanceAPIConnector.getBalanceAtBlockHeight(address, latestBlock.data.height)
44-
45-
Assertions.assertNotNull(balanceAtHeight, "Balance at specific block height should not be null")
46-
Assertions.assertTrue(balanceAtHeight >= 0, "Balance at specific block height should be non-negative")
47-
}
48-
is FlowAccessApi.AccessApiCallResponse.Error -> Assertions.fail("Failed to retrieve the latest block: ${latestBlock.message}")
49-
}
45+
Assertions.assertNotNull(balanceAtHeight, "Balance at specific block height should not be null")
46+
Assertions.assertTrue(balanceAtHeight >= 0, "Balance at specific block height should be non-negative")
5047
}
5148

5249
@Test
@@ -56,18 +53,11 @@ internal class GetAccountBalanceAccessAPIConnectorTest {
5653
// Fetch balance at latest block
5754
val balanceAtLatest = balanceAPIConnector.getBalanceAtLatestBlock(address)
5855

59-
// Fetch latest block height
60-
val latestBlock = accessAPI.getLatestBlock(true)
61-
when (latestBlock) {
62-
is FlowAccessApi.AccessApiCallResponse.Success -> {
63-
val blockHeight = latestBlock.data.height
56+
val blockHeight = latestBlock.height
6457

65-
// Fetch balance at the same block height
66-
val balanceAtHeight = balanceAPIConnector.getBalanceAtBlockHeight(address, blockHeight)
58+
// Fetch balance at the same block height
59+
val balanceAtHeight = balanceAPIConnector.getBalanceAtBlockHeight(address, blockHeight)
6760

68-
Assertions.assertEquals(balanceAtLatest, balanceAtHeight, "Balance at latest block and specific block height should match")
69-
}
70-
is FlowAccessApi.AccessApiCallResponse.Error -> Assertions.fail("Failed to retrieve the latest block: ${latestBlock.message}")
71-
}
61+
Assertions.assertEquals(balanceAtLatest, balanceAtHeight, "Balance at latest block and specific block height should match")
7262
}
7363
}

kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getCollection/GetCollectionAccessAPIConnectorTest.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import org.junit.jupiter.api.Assertions.*
44
import org.junit.jupiter.api.BeforeEach
55
import org.junit.jupiter.api.Test
66
import org.onflow.examples.kotlin.AccessAPIConnector
7+
import org.onflow.examples.kotlin.getTransaction.GetTransactionAccessAPIConnectorTest
78
import org.onflow.flow.common.test.FlowEmulatorProjectTest
89
import org.onflow.flow.common.test.FlowServiceAccountCredentials
910
import org.onflow.flow.common.test.FlowTestClient
@@ -23,6 +24,7 @@ class GetCollectionAccessAPIConnectorTest {
2324
private lateinit var accessAPIConnector: AccessAPIConnector
2425

2526
private lateinit var collectionId: FlowId
27+
lateinit var block: FlowBlock
2628

2729
@BeforeEach
2830
fun setup() {
@@ -36,10 +38,7 @@ class GetCollectionAccessAPIConnectorTest {
3638
publicKey
3739
)
3840

39-
val block = when (val response = accessAPI.getLatestBlock()) {
40-
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
41-
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
42-
}
41+
block = GetTransactionAccessAPIConnectorTest.fetchLatestBlockWithRetries(accessAPI)
4342
collectionId = block.collectionGuarantees.first().id
4443
}
4544

kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getProtocolState/GetProtocolStateAccessAPIConnectorTest.kt

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.onflow.examples.kotlin.getProtocolState
33
import org.junit.jupiter.api.Assertions.*
44
import org.junit.jupiter.api.BeforeEach
55
import org.junit.jupiter.api.Test
6+
import org.onflow.examples.kotlin.getTransaction.GetTransactionAccessAPIConnectorTest
67
import org.onflow.flow.common.test.FlowEmulatorProjectTest
78
import org.onflow.flow.common.test.FlowTestClient
89
import org.onflow.flow.sdk.FlowAccessApi
@@ -20,6 +21,7 @@ internal class GetProtocolStateAccessAPIConnectorTest {
2021
@BeforeEach
2122
fun setup() {
2223
protocolStateConnector = GetProtocolStateAccessAPIConnector(accessAPI)
24+
block = GetTransactionAccessAPIConnectorTest.fetchLatestBlockWithRetries(accessAPI)
2325
}
2426

2527
@Test
@@ -30,22 +32,12 @@ internal class GetProtocolStateAccessAPIConnectorTest {
3032

3133
@Test
3234
fun `Can get protocol state snapshot by blockId`() {
33-
block = when (val response = accessAPI.getLatestBlock()) {
34-
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
35-
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
36-
}
37-
3835
val latestSnapshot: FlowSnapshot = protocolStateConnector.getProtocolStateSnapshotByBlockId(block.id)
3936
assertNotNull(latestSnapshot, ("Snapshot should not be null"))
4037
}
4138

4239
@Test
4340
fun `Can get protocol state snapshot by height`() {
44-
block = when (val response = accessAPI.getLatestBlock()) {
45-
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
46-
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
47-
}
48-
4941
val latestSnapshot: FlowSnapshot = protocolStateConnector.getProtocolStateSnapshotByHeight(block.height)
5042
assertNotNull(latestSnapshot, ("Snapshot should not be null"))
5143
}

kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getTransaction/GetTransactionAccessAPIConnectorTest.kt

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.onflow.examples.kotlin.getTransaction
22

3+
import kotlinx.coroutines.delay
4+
import kotlinx.coroutines.runBlocking
35
import org.junit.jupiter.api.Assertions.*
46
import org.junit.jupiter.api.BeforeEach
57
import org.junit.jupiter.api.Test
@@ -37,10 +39,7 @@ internal class GetTransactionAccessAPIConnectorTest {
3739
publicKey
3840
)
3941

40-
block = when (val response = accessAPI.getLatestBlock()) {
41-
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
42-
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
43-
}
42+
block = fetchLatestBlockWithRetries(accessAPI)
4443
}
4544

4645
@Test
@@ -66,4 +65,19 @@ internal class GetTransactionAccessAPIConnectorTest {
6665
assertNotNull(transactionResult, "Transaction result should not be null")
6766
assertTrue(transactionResult.status === FlowTransactionStatus.SEALED, "Transaction should be sealed")
6867
}
68+
69+
companion object {
70+
fun fetchLatestBlockWithRetries(accessAPI: FlowAccessApi, retries: Int = 5, delayMillis: Long = 500): FlowBlock {
71+
repeat(retries) { attempt ->
72+
when (val response = accessAPI.getLatestBlock()) {
73+
is FlowAccessApi.AccessApiCallResponse.Success -> return response.data
74+
is FlowAccessApi.AccessApiCallResponse.Error -> {
75+
println("Attempt ${attempt + 1} failed: ${response.message}. Retrying...")
76+
runBlocking { delay(delayMillis) }
77+
}
78+
}
79+
}
80+
throw Exception("Failed to retrieve the latest block after $retries attempts.")
81+
}
82+
}
6983
}

sdk/src/intTest/org/onflow/flow/sdk/transaction/TransactionIntegrationTest.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.onflow.flow.sdk.transaction
22

3+
import kotlinx.coroutines.delay
4+
import kotlinx.coroutines.runBlocking
35
import org.onflow.flow.sdk.*
46
import org.assertj.core.api.Assertions.assertThat
57
import org.junit.jupiter.api.Test
@@ -43,8 +45,22 @@ class TransactionIntegrationTest {
4345
fail("$errorMessage: ${e.message}")
4446
}
4547

46-
private fun getLatestBlock(): FlowBlock =
47-
safelyHandle({ Result.success(handleResult(accessAPI.getLatestBlock(true), LATEST_BLOCK_ERROR)) }, LATEST_BLOCK_ERROR)
48+
private fun getLatestBlock(retries: Int = 5, delayMillis: Long = 500): FlowBlock {
49+
repeat(retries) { attempt ->
50+
try {
51+
return safelyHandle(
52+
{ Result.success(handleResult(accessAPI.getLatestBlock(true), LATEST_BLOCK_ERROR)) },
53+
LATEST_BLOCK_ERROR
54+
)
55+
} catch (e: Exception) {
56+
if (attempt == retries - 1) {
57+
throw Exception("$LATEST_BLOCK_ERROR after $retries attempts", e)
58+
}
59+
runBlocking { delay(delayMillis) }
60+
}
61+
}
62+
throw Exception(LATEST_BLOCK_ERROR)
63+
}
4864

4965
private fun getAccountAtLatestBlock(address: FlowAddress): FlowAccount =
5066
safelyHandle({ Result.success(handleResult(accessAPI.getAccountAtLatestBlock(address), ACCOUNT_ERROR)) }, ACCOUNT_ERROR)

sdk/src/main/kotlin/org/onflow/flow/sdk/AsyncFlowAccessApi.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ interface AsyncFlowAccessApi {
4040

4141
fun getTransactionResultById(id: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowTransactionResult?>>
4242

43+
fun getSystemTransaction(blockId: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowTransaction?>>
44+
45+
fun getSystemTransactionResult(blockId: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowTransactionResult?>>
46+
4347
fun getTransactionResultByIndex(blockId: FlowId, index: Int): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowTransactionResult>>
4448

4549
@Deprecated(

sdk/src/main/kotlin/org/onflow/flow/sdk/FlowAccessApi.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ interface FlowAccessApi {
5252

5353
fun getTransactionResultById(id: FlowId): AccessApiCallResponse<FlowTransactionResult>
5454

55+
fun getSystemTransaction(blockId: FlowId): AccessApiCallResponse<FlowTransaction>
56+
57+
fun getSystemTransactionResult(blockId: FlowId): AccessApiCallResponse<FlowTransactionResult>
58+
5559
fun getTransactionResultByIndex(blockId: FlowId, index: Int): AccessApiCallResponse<FlowTransactionResult>
5660

5761
@Deprecated(

sdk/src/main/kotlin/org/onflow/flow/sdk/impl/AsyncFlowAccessApiImpl.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,34 @@ class AsyncFlowAccessApiImpl(
356356
errorMessage = "Failed to get transaction result by index"
357357
)
358358

359+
override fun getSystemTransaction(blockId: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowTransaction?>> =
360+
handleApiCall(
361+
apiCall = {
362+
api.getSystemTransaction(
363+
Access.GetSystemTransactionRequest
364+
.newBuilder()
365+
.setBlockId(blockId.byteStringValue)
366+
.build()
367+
)
368+
},
369+
transform = { if (it.hasTransaction()) FlowTransaction.of(it.transaction) else null },
370+
errorMessage = "Failed to get system transaction by block ID"
371+
)
372+
373+
override fun getSystemTransactionResult(blockId: FlowId): CompletableFuture<FlowAccessApi.AccessApiCallResponse<FlowTransactionResult?>> =
374+
handleApiCall(
375+
apiCall = {
376+
api.getSystemTransactionResult(
377+
Access.GetSystemTransactionResultRequest
378+
.newBuilder()
379+
.setBlockId(blockId.byteStringValue)
380+
.build()
381+
)
382+
},
383+
transform = { FlowTransactionResult.of(it) },
384+
errorMessage = "Failed to get system transaction result by block ID"
385+
)
386+
359387
private fun executeScript(
360388
apiCall: () -> ListenableFuture<Access.ExecuteScriptResponse>,
361389
errorMessage: String

0 commit comments

Comments
 (0)