Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Examples use-cases - get blocks #80

Merged
merged 33 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ab1148d
Get block example - Kotlin
lealobanov Aug 15, 2024
799fa4d
Get block example - Kotlin
lealobanov Aug 15, 2024
fe68568
Get block example - Kotlin
lealobanov Aug 15, 2024
d6173b3
Linting
lealobanov Aug 15, 2024
2403612
Linting
lealobanov Aug 15, 2024
1ae92e5
Linting
lealobanov Aug 16, 2024
350619a
Examples use-cases - get account
lealobanov Aug 17, 2024
8fd9402
Examples use-cases - get account
lealobanov Aug 17, 2024
e670b99
Linting
lealobanov Aug 17, 2024
c74eed6
Examples use-cases - get transaction
lealobanov Aug 17, 2024
0fae809
Examples use-cases - get transaction
lealobanov Aug 17, 2024
ab36738
Linting
lealobanov Aug 17, 2024
d83b9e5
WIP: get event example
lealobanov Aug 18, 2024
bf29aa8
Linting
lealobanov Aug 18, 2024
acef276
Linting
lealobanov Aug 18, 2024
1f0cd27
Linting
lealobanov Aug 18, 2024
555a011
Linting
lealobanov Aug 18, 2024
4c54d10
WIP: Java get event example
lealobanov Aug 18, 2024
753decf
WIP: Java get event example
lealobanov Aug 18, 2024
b772815
WIP: Java get event example
lealobanov Aug 18, 2024
583e282
WIP: Java get event example
lealobanov Aug 18, 2024
b3820e3
Get network parameters - Kotlin example
lealobanov Aug 19, 2024
d108a41
Get network parameters - Kotlin example
lealobanov Aug 19, 2024
fffcf98
Get network parameters - Java example
lealobanov Aug 19, 2024
71b47fb
Get network parameters - Java example
lealobanov Aug 19, 2024
4c670fe
Get network parameters - Java example
lealobanov Aug 19, 2024
4fff2c5
Linting
lealobanov Aug 19, 2024
010d55b
Merge pull request #86 from onflow/get-network-params-example
lealobanov Aug 20, 2024
51ecee5
Merge pull request #84 from onflow/get-event-example
lealobanov Aug 20, 2024
1c9e90c
Merge pull request #83 from onflow/get-transaction-example
lealobanov Aug 20, 2024
6ef59c3
Merge pull request #82 from onflow/get-account-example
lealobanov Aug 20, 2024
8ebb852
Merge branch 'main' into get-blocks-example
lealobanov Aug 20, 2024
2c0710a
Debug failing test gas limit
lealobanov Aug 20, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ object FlowTestUtil {
val fileLocation = flowJsonLocation?.let(::File)
if (fileLocation != null && fileLocation.exists() && fileLocation.isFile) {
flowJson = fileLocation.absolutePath
println("Flow JSON found as file: $flowJson")
}

// Check if flowJsonLocation is a classpath resource
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,14 @@ private FlowTransactionResult waitForSeal(FlowId txID) {

private FlowAddress getAccountCreatedAddress(FlowTransactionResult txResult) {
FlowEvent event = txResult.getEvents().stream()
.filter(it -> "flow.AccountCreated".equals(it.getType()))
.filter(it -> "flow.AccountCreated".equals(it.getType()))
.findFirst()
.orElseThrow(() -> new RuntimeException("account created event not found"));

EventField field = (EventField) event.getPayload().getJsonCadence();
String address = (String) field.getValue().getRequiredField("address").getValue();

assert address != null;
return new FlowAddress(address);
}

Expand All @@ -103,18 +104,10 @@ private byte[] loadScript(String name) {
}
}

public FlowAddress createAccount(FlowAddress payerAddress, PublicKey publicKey) {

FlowAccountKey payerAccountKey = getAccountKey(payerAddress, 0);

FlowScript script = new FlowScript(loadScript("cadence/create_account.cdc"));

FlowTransaction tx = new FlowTransaction(
private FlowTransaction createTransaction(FlowAddress payerAddress, FlowAccountKey payerAccountKey, FlowScript script, List<FlowArgument> arguments) {
return new FlowTransaction(
script,
List.of(
new FlowArgument(new StringField(publicKey.getHex())),
new FlowArgument(new UInt8NumberField(Integer.toString(publicKey.getAlgo().getIndex())))
),
arguments,
getLatestBlockID(),
500L,
new FlowTransactionProposalKey(
Expand All @@ -127,50 +120,52 @@ public FlowAddress createAccount(FlowAddress payerAddress, PublicKey publicKey)
Collections.emptyList(),
Collections.emptyList()
);
}

private FlowId signAndSendTransaction(FlowTransaction tx, FlowAddress payerAddress, FlowAccountKey payerAccountKey) {
Signer signer = Crypto.getSigner(privateKey, payerAccountKey.getHashAlgo());
tx = tx.addEnvelopeSignature(payerAddress, payerAccountKey.getId(), signer);
return sendTransaction(tx);
}

FlowId txID = sendTransaction(tx);

public FlowAddress createAccount(FlowAddress payerAddress, PublicKey publicKey) {
FlowAccountKey payerAccountKey = getAccountKey(payerAddress, 0);
FlowScript script = new FlowScript(loadScript("cadence/create_account.cdc"));
List<FlowArgument> arguments = List.of(
new FlowArgument(new StringField(publicKey.getHex())),
new FlowArgument(new UInt8NumberField(Integer.toString(publicKey.getAlgo().getIndex())))
);
FlowTransaction tx = createTransaction(payerAddress, payerAccountKey, script, arguments);
FlowId txID = signAndSendTransaction(tx, payerAddress, payerAccountKey);
FlowTransactionResult txResult = waitForSeal(txID);
return getAccountCreatedAddress(txResult);
}

public FlowId sendSampleTransaction(FlowAddress payerAddress, PublicKey publicKey) {
FlowAccountKey payerAccountKey = getAccountKey(payerAddress, 0);
FlowScript script = new FlowScript(loadScript("cadence/create_account.cdc"));
List<FlowArgument> arguments = List.of(
new FlowArgument(new StringField(publicKey.getHex())),
new FlowArgument(new UInt8NumberField(Integer.toString(publicKey.getAlgo().getIndex())))
);
FlowTransaction tx = createTransaction(payerAddress, payerAccountKey, script, arguments);
return signAndSendTransaction(tx, payerAddress, payerAccountKey);
}

public void transferTokens(FlowAddress senderAddress, FlowAddress recipientAddress, BigDecimal amount) {
if (amount.scale() != 8) {
throw new RuntimeException("FLOW amount must have exactly 8 decimal places of precision (e.g. 10.00000000)");
}

FlowAccountKey senderAccountKey = getAccountKey(senderAddress, 0);

FlowScript script = new FlowScript(loadScript("cadence/transfer_flow.cdc"));

List<FlowArgument> arguments = List.of(
new FlowArgument(new UFix64NumberField(amount.toPlainString())),
new FlowArgument(new AddressField(recipientAddress.getBase16Value()))
);

FlowTransaction tx = new FlowTransaction(
script,
arguments,
getLatestBlockID(),
500L,
new FlowTransactionProposalKey(
senderAddress,
senderAccountKey.getId(),
senderAccountKey.getSequenceNumber()
),
senderAddress,
Collections.singletonList(senderAddress),
Collections.emptyList(),
Collections.emptyList()
);

Signer signer = Crypto.getSigner(privateKey, senderAccountKey.getHashAlgo());
tx = tx.addEnvelopeSignature(senderAddress, senderAccountKey.getId(), signer);

FlowId txID = sendTransaction(tx);
FlowTransaction tx = createTransaction(senderAddress, senderAccountKey, script, arguments);
FlowId txID = signAndSendTransaction(tx, senderAddress, senderAccountKey);
waitForSeal(txID);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.onflow.examples.java.getAccount;

import org.onflow.flow.sdk.*;
import java.math.BigDecimal;

public class GetAccountAccessAPIConnector {

private final FlowAccessApi accessAPI;

public GetAccountAccessAPIConnector(FlowAccessApi accessAPI) {
this.accessAPI = accessAPI;
}

public FlowAccount getAccountAtLatestBlock(FlowAddress address) {
FlowAccessApi.AccessApiCallResponse<FlowAccount> response = accessAPI.getAccountAtLatestBlock(address);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowAccount>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}

public FlowAccount getAccountAtBlockHeight(FlowAddress address, long height) {
FlowAccessApi.AccessApiCallResponse<FlowAccount> response = accessAPI.getAccountByBlockHeight(address, height);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowAccount>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}

public BigDecimal getAccountBalance(FlowAddress address) {
FlowAccount account = getAccountAtLatestBlock(address);
return account.getBalance();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.onflow.examples.java.getBlock;

import org.onflow.flow.sdk.*;

public class GetBlockAccessAPIConnector {

private final FlowAccessApi accessAPI;

public GetBlockAccessAPIConnector(FlowAccessApi accessAPI) {
this.accessAPI = accessAPI;
}

public FlowBlock getLatestSealedBlock() {
boolean isSealed = true;
FlowAccessApi.AccessApiCallResponse<FlowBlock> response = accessAPI.getLatestBlock(isSealed);

if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowBlock>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}

public FlowBlock getBlockByID(FlowId blockID) {
FlowAccessApi.AccessApiCallResponse<FlowBlock> response = accessAPI.getBlockById(blockID);

if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowBlock>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}

public FlowBlock getBlockByHeight(long height) {
FlowAccessApi.AccessApiCallResponse<FlowBlock> response = accessAPI.getBlockByHeight(height);

if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowBlock>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.onflow.examples.java.getEvent;

import kotlin.ranges.LongRange;
import org.onflow.flow.sdk.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class GetEventAccessAPIConnector {
private final FlowAccessApi accessAPI;

public GetEventAccessAPIConnector(FlowAccessApi accessAPI) {
this.accessAPI = accessAPI;
}

public List<FlowEventResult> getEventsForHeightRange(String eventType, long startHeight, long endHeight) {
LongRange range = new LongRange(startHeight, endHeight);
FlowAccessApi.AccessApiCallResponse<List<FlowEventResult>> response = accessAPI.getEventsForHeightRange(eventType, range);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<List<FlowEventResult>>) response).getData();
} else if (response instanceof FlowAccessApi.AccessApiCallResponse.Error) {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
} else {
throw new RuntimeException("Unknown response type");
}
}
lealobanov marked this conversation as resolved.
Show resolved Hide resolved

public List<FlowEventResult> getEventsForBlockIds(String eventType, List<FlowId> blockIds) {
Set<FlowId> blockIdSet = new HashSet<>(blockIds);
FlowAccessApi.AccessApiCallResponse<List<FlowEventResult>> response = accessAPI.getEventsForBlockIds(eventType, blockIdSet);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<List<FlowEventResult>>) response).getData();
} else if (response instanceof FlowAccessApi.AccessApiCallResponse.Error errorResponse) {
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
} else {
throw new RuntimeException("Unknown response type");
}
}
Comment on lines +29 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enhance type safety with Java Generics.

In getEventsForBlockIds, consider using Java Generics to improve type safety and reduce casting.

// Current casting
return ((FlowAccessApi.AccessApiCallResponse.Success<List<FlowEventResult>>) response).getData();

// Suggested use of generics
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success<List<FlowEventResult>> successResponse) {
    return successResponse.getData();
}


public FlowTransactionResult getTransactionResult(FlowId txID) {
FlowAccessApi.AccessApiCallResponse<FlowTransactionResult> response = accessAPI.getTransactionResultById(txID);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowTransactionResult>) response).getData();
} else if (response instanceof FlowAccessApi.AccessApiCallResponse.Error errorResponse) {
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
} else {
throw new RuntimeException("Unknown response type");
}
}
lealobanov marked this conversation as resolved.
Show resolved Hide resolved

public List<FlowEventResult> getAccountCreatedEvents(long startHeight, long endHeight) {
return getEventsForHeightRange("flow.AccountCreated", startHeight, endHeight);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.onflow.examples.java.getNetworkParams;

import org.onflow.flow.sdk.FlowAccessApi;
import org.onflow.flow.sdk.FlowChainId;

public class GetNetworkParametersAccessAPIConnector {

private final FlowAccessApi accessAPI;

public GetNetworkParametersAccessAPIConnector(FlowAccessApi accessAPI) {
this.accessAPI = accessAPI;
}

public FlowChainId getNetworkParameters() {
FlowAccessApi.AccessApiCallResponse<FlowChainId> response = accessAPI.getNetworkParameters();
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowChainId>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
lealobanov marked this conversation as resolved.
Show resolved Hide resolved
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.onflow.examples.java.getTransaction;

import org.onflow.flow.sdk.FlowAccessApi;
import org.onflow.flow.sdk.FlowId;
import org.onflow.flow.sdk.FlowTransaction;
import org.onflow.flow.sdk.FlowTransactionResult;

public class GetTransactionAccessAPIConnector {

private final FlowAccessApi accessAPI;

public GetTransactionAccessAPIConnector(FlowAccessApi accessAPI) {
this.accessAPI = accessAPI;
}

public FlowTransaction getTransaction(FlowId txID) {
FlowAccessApi.AccessApiCallResponse<FlowTransaction> response = accessAPI.getTransactionById(txID);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowTransaction>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}
lealobanov marked this conversation as resolved.
Show resolved Hide resolved

public FlowTransactionResult getTransactionResult(FlowId txID) {
FlowAccessApi.AccessApiCallResponse<FlowTransactionResult> response = accessAPI.getTransactionResultById(txID);
if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) {
return ((FlowAccessApi.AccessApiCallResponse.Success<FlowTransactionResult>) response).getData();
} else {
FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
}
}
Comment on lines +26 to +34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider handling specific exceptions.

The method getTransactionResult throws a generic RuntimeException on error. Consider handling specific exceptions to provide more context-specific error handling.

// Example of handling specific exceptions
if (response instanceof FlowAccessApi.AccessApiCallResponse.Error) {
    FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response;
    if (errorResponse.getThrowable() instanceof SpecificException) {
        // Handle specific exception
    } else {
        throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable());
    }
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.onflow.examples.java.getAccount;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.onflow.flow.common.test.FlowEmulatorProjectTest;
import org.onflow.flow.common.test.FlowServiceAccountCredentials;
import org.onflow.flow.common.test.FlowTestClient;
import org.onflow.flow.common.test.TestAccount;
import org.onflow.flow.sdk.FlowAccessApi;
import org.onflow.flow.sdk.FlowAccount;
import org.onflow.flow.sdk.FlowAddress;

import java.math.BigDecimal;

@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json")
public class GetAccountAccessAPIConnectorTest {
@FlowServiceAccountCredentials
private TestAccount testAccount;

@FlowTestClient
private FlowAccessApi accessAPI;

private GetAccountAccessAPIConnector accountConnector;

@BeforeEach
public void setup() {
accountConnector = new GetAccountAccessAPIConnector(accessAPI);
}

@Test
public void testCanFetchAccountFromLatestBlock() {
FlowAddress address = testAccount.getFlowAddress();
FlowAccount account = accountConnector.getAccountAtLatestBlock(address);

Assertions.assertNotNull(account, "Account should not be null");
Assertions.assertEquals(address.getBase16Value(), account.getAddress().getBase16Value(), "Address should match");
Assertions.assertTrue(account.getBalance().compareTo(BigDecimal.ZERO) >= 0, "Account balance should be non-negative");
}

@Test
public void testCanFetchAccountFromBlockByHeight0() {
FlowAddress address = testAccount.getFlowAddress();
FlowAccount account = accountConnector.getAccountAtBlockHeight(address, 0);

Assertions.assertNotNull(account, "Account should not be null");
Assertions.assertEquals(address.getBase16Value(), account.getAddress().getBase16Value(), "Address should match");
Assertions.assertTrue(account.getBalance().compareTo(BigDecimal.ZERO) >= 0, "Account balance should be non-negative");
}

@Test
public void testCanFetchAccountBalance() {
FlowAddress address = testAccount.getFlowAddress();
BigDecimal balance = accountConnector.getAccountBalance(address);

Assertions.assertNotNull(balance, "Balance should not be null");
Assertions.assertTrue(balance.compareTo(BigDecimal.ZERO) >= 0, "Balance should be non-negative");
}
}
Loading
Loading