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 transaction #83

Merged
merged 20 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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 @@ -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(),
100L,
new FlowTransactionProposalKey(
Expand All @@ -127,50 +120,51 @@ 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(),
100L,
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,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());
}
}

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());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.onflow.examples.java.getTransaction;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.onflow.examples.java.AccessAPIConnector;
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.FlowId;
import org.onflow.flow.sdk.FlowTransaction;
import org.onflow.flow.sdk.FlowTransactionResult;
import org.onflow.flow.sdk.FlowTransactionStatus;
import org.onflow.flow.sdk.crypto.Crypto;
import org.onflow.flow.sdk.crypto.PublicKey;
import org.onflow.flow.sdk.SignatureAlgorithm;

import static org.junit.jupiter.api.Assertions.*;

@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json")
public class GetTransactionAccessAPIConnectorTest {

@FlowServiceAccountCredentials
private TestAccount serviceAccount;

@FlowTestClient
private FlowAccessApi accessAPI;

private GetTransactionAccessAPIConnector connector;
private AccessAPIConnector accessAPIConnector;

private FlowId txID;

@BeforeEach
public void setup() {
accessAPIConnector = new AccessAPIConnector(serviceAccount.getPrivateKey(), accessAPI);
connector = new GetTransactionAccessAPIConnector(accessAPI);

// Send a sample transaction to create an account and capture the transaction ID
PublicKey publicKey = Crypto.generateKeyPair(SignatureAlgorithm.ECDSA_P256).getPublic();
txID = accessAPIConnector.sendSampleTransaction(
serviceAccount.getFlowAddress(),
publicKey
);
}

@Test
public void canFetchTransaction() {
FlowTransaction transaction = connector.getTransaction(txID);

assertNotNull(transaction, "Transaction should not be null");
assertEquals(txID, transaction.getId(), "Transaction ID should match");
}

@Test
public void canFetchTransactionResult() {
FlowTransactionResult transactionResult = connector.getTransactionResult(txID);

assertNotNull(transactionResult, "Transaction result should not be null");
assertSame(transactionResult.getStatus(), FlowTransactionStatus.SEALED, "Transaction should be sealed");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,42 @@ internal class AccessAPIConnector(
return getAccountCreatedAddress(txResult)
}

fun sendSampleTransaction(
payerAddress: FlowAddress,
publicKey: PublicKey,
scriptName: String = "cadence/create_account.cdc",
gasLimit: Long = 100
): FlowId {
val payerAccountKey = getAccountKey(payerAddress, 0)

var tx = FlowTransaction(
script = FlowScript(loadScript(scriptName)),
arguments = listOf(
FlowArgument(StringField(publicKey.hex)),
FlowArgument(UInt8NumberField(publicKey.algo.index.toString()))
),
referenceBlockId = latestBlockID,
gasLimit = gasLimit,
proposalKey = FlowTransactionProposalKey(
address = payerAddress,
keyIndex = payerAccountKey.id,
sequenceNumber = payerAccountKey.sequenceNumber.toLong()
),
payerAddress = payerAddress,
authorizers = listOf(payerAddress)
)

val signer = Crypto.getSigner(privateKey, payerAccountKey.hashAlgo)
tx = tx.addEnvelopeSignature(payerAddress, payerAccountKey.id, signer)

val txID = when (val response = accessAPI.sendTransaction(tx)) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}

return txID
}

fun transferTokens(senderAddress: FlowAddress, recipientAddress: FlowAddress, amount: BigDecimal) {
if (amount.scale() != 8) {
throw Exception("FLOW amount must have exactly 8 decimal places of precision (e.g. 10.00000000)")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.onflow.examples.kotlin.getTransaction

import org.onflow.flow.sdk.*

class GetTransactionAccessAPIConnector(
private val accessAPI: FlowAccessApi
) {
fun getTransaction(txID: FlowId): FlowTransaction {
return when (val response = accessAPI.getTransactionById(txID)) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}
}

fun getTransactionResult(txID: FlowId): FlowTransactionResult {
return when (val response = accessAPI.getTransactionResultById(txID)) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.onflow.examples.kotlin.getTransaction

import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.onflow.examples.kotlin.AccessAPIConnector
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.*
import org.onflow.flow.sdk.crypto.Crypto

@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json")
internal class GetTransactionAccessAPIConnectorTest {
@FlowServiceAccountCredentials
lateinit var serviceAccount: TestAccount

@FlowTestClient
lateinit var accessAPI: FlowAccessApi

private lateinit var connector: GetTransactionAccessAPIConnector
private lateinit var accessAPIConnector: AccessAPIConnector

private lateinit var txID: FlowId

@BeforeEach
fun setup() {
accessAPIConnector = AccessAPIConnector(serviceAccount.privateKey, accessAPI)
connector = GetTransactionAccessAPIConnector(accessAPI)

// Send a sample transaction to create an account and capture the transaction ID
val publicKey = Crypto.generateKeyPair(SignatureAlgorithm.ECDSA_P256).public
txID = accessAPIConnector.sendSampleTransaction(
serviceAccount.flowAddress,
publicKey
)
}

@Test
fun `Can fetch a transaction`() {
val transaction = connector.getTransaction(txID)

assertNotNull(transaction, "Transaction should not be null")
assertEquals(txID, transaction.id, "Transaction ID should match")
}

@Test
fun `Can fetch a transaction result`() {
val transactionResult = connector.getTransactionResult(txID)

assertNotNull(transactionResult, "Transaction result should not be null")
assertTrue(transactionResult.status === FlowTransactionStatus.SEALED, "Transaction should be sealed")
}
}
Loading