diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..16afe5dd3 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,15 @@ +Algorand Java SDK Examples + + + +Running +-------- +Package with `mvn` +```sh +mvn package +``` + +Run an example with java +```sh +java -cp target/sdk-extras-1.0-SNAPSHOT.jar com.algorand.examples.Example +``` \ No newline at end of file diff --git a/examples/application/approval.teal b/examples/application/approval.teal new file mode 100644 index 000000000..07fca803f --- /dev/null +++ b/examples/application/approval.teal @@ -0,0 +1,100 @@ +#pragma version 4 +// Handle each possible OnCompletion type. We don't have to worry about +// handling ClearState, because the ClearStateProgram will execute in that +// case, not the ApprovalProgram. +txn ApplicationID +int 0 +== +bnz handle_approve + +txn OnCompletion +int NoOp +== +bnz handle_noop + +txn OnCompletion +int OptIn +== +bnz handle_approve + +txn OnCompletion +int CloseOut +== +bnz handle_closeout + +txn OnCompletion +int UpdateApplication +== +bnz handle_updateapp + +txn OnCompletion +int DeleteApplication +== +bnz handle_deleteapp + +// Unexpected OnCompletion value. Should be unreachable. +err + +handle_noop: +// Handle NoOp + +// read global state +byte "counter" +dup +app_global_get + +// increment the value +int 1 ++ + +// store to scratch space +dup +store 0 + +// update global state +app_global_put + +// read local state for sender +int 0 +byte "counter" +app_local_get + +// increment the value +int 1 ++ +store 1 + +// update local state for sender +int 0 +byte "counter" +load 1 +app_local_put + +// load return value as approval +load 0 +return + + +handle_closeout: +// Handle CloseOut +//approval +int 1 +return + +handle_deleteapp: +// Check for creator +global CreatorAddress +txn Sender +== +return + +handle_updateapp: +// Check for creator +global CreatorAddress +txn Sender +== +return + +handle_approve: +int 1 +return \ No newline at end of file diff --git a/examples/application/approval_refactored.teal b/examples/application/approval_refactored.teal new file mode 100644 index 000000000..1af5a7ebe --- /dev/null +++ b/examples/application/approval_refactored.teal @@ -0,0 +1,107 @@ +#pragma version 4 +// Handle each possible OnCompletion type. We don't have to worry about +// handling ClearState, because the ClearStateProgram will execute in that +// case, not the ApprovalProgram. + +txn ApplicationID +int 0 +== +bnz handle_approve + +txn OnCompletion +int NoOp +== +bnz handle_noop + +txn OnCompletion +int OptIn +== +bnz handle_approve + +txn OnCompletion +int CloseOut +== +bnz handle_closeout + +txn OnCompletion +int UpdateApplication +== +bnz handle_updateapp + +txn OnCompletion +int DeleteApplication +== +bnz handle_deleteapp + +// Unexpected OnCompletion value. Should be unreachable. +err + +handle_noop: +// Handle NoOp + +// read global state +byte "counter" +dup +app_global_get + +// increment the value +int 1 ++ + +// store to scratch space +dup +store 0 + +// update global state +app_global_put + +// read local state for sender +int 0 +byte "counter" +app_local_get + +// increment the value +int 1 ++ +store 1 + +// update local state for sender +// update "counter" +int 0 +byte "counter" +load 1 +app_local_put + +// update "timestamp" +int 0 +byte "timestamp" +txn ApplicationArgs 0 +app_local_put + +// load return value as approval +load 0 +return + +handle_closeout: +// Handle CloseOut +//approval +int 1 +return + +handle_deleteapp: +// Check for creator +global CreatorAddress +txn Sender +== +return + +handle_updateapp: +// Check for creator +global CreatorAddress +txn Sender +== +return + +handle_approve: +int 1 +return \ No newline at end of file diff --git a/examples/application/clear.teal b/examples/application/clear.teal new file mode 100644 index 000000000..d793651c8 --- /dev/null +++ b/examples/application/clear.teal @@ -0,0 +1,3 @@ +#pragma version 4 +int 1 +return \ No newline at end of file diff --git a/examples/calculator/approval.teal b/examples/calculator/approval.teal new file mode 100644 index 000000000..32acbb840 --- /dev/null +++ b/examples/calculator/approval.teal @@ -0,0 +1,181 @@ +#pragma version 8 +intcblock 0 1 +bytecblock 0x151f7c75 +txn NumAppArgs +intc_0 // 0 +== +bnz main_l10 +txna ApplicationArgs 0 +pushbytes 0xfe6bdf69 // "add(uint64,uint64)uint64" +== +bnz main_l9 +txna ApplicationArgs 0 +pushbytes 0xe2f188c5 // "mul(uint64,uint64)uint64" +== +bnz main_l8 +txna ApplicationArgs 0 +pushbytes 0x78b488b7 // "sub(uint64,uint64)uint64" +== +bnz main_l7 +txna ApplicationArgs 0 +pushbytes 0x16e80f08 // "div(uint64,uint64)uint64" +== +bnz main_l6 +err +main_l6: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 9 +txna ApplicationArgs 2 +btoi +store 10 +load 9 +load 10 +callsub div_3 +store 11 +bytec_0 // 0x151f7c75 +load 11 +itob +concat +log +intc_1 // 1 +return +main_l7: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 6 +txna ApplicationArgs 2 +btoi +store 7 +load 6 +load 7 +callsub sub_2 +store 8 +bytec_0 // 0x151f7c75 +load 8 +itob +concat +log +intc_1 // 1 +return +main_l8: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 3 +txna ApplicationArgs 2 +btoi +store 4 +load 3 +load 4 +callsub mul_1 +store 5 +bytec_0 // 0x151f7c75 +load 5 +itob +concat +log +intc_1 // 1 +return +main_l9: +txn OnCompletion +intc_0 // NoOp +== +txn ApplicationID +intc_0 // 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 0 +txna ApplicationArgs 2 +btoi +store 1 +load 0 +load 1 +callsub add_0 +store 2 +bytec_0 // 0x151f7c75 +load 2 +itob +concat +log +intc_1 // 1 +return +main_l10: +txn OnCompletion +intc_0 // NoOp +== +bnz main_l12 +err +main_l12: +txn ApplicationID +intc_0 // 0 +== +assert +intc_1 // 1 +return + +// add +add_0: +proto 2 1 +intc_0 // 0 +frame_dig -2 +frame_dig -1 ++ +frame_bury 0 +retsub + +// mul +mul_1: +proto 2 1 +intc_0 // 0 +frame_dig -2 +frame_dig -1 +* +frame_bury 0 +retsub + +// sub +sub_2: +proto 2 1 +intc_0 // 0 +frame_dig -2 +frame_dig -1 +- +frame_bury 0 +retsub + +// div +div_3: +proto 2 1 +intc_0 // 0 +frame_dig -2 +frame_dig -1 +/ +frame_bury 0 +retsub \ No newline at end of file diff --git a/examples/calculator/clear.teal b/examples/calculator/clear.teal new file mode 100644 index 000000000..e741f0e57 --- /dev/null +++ b/examples/calculator/clear.teal @@ -0,0 +1,3 @@ +#pragma version 8 +pushint 0 // 0 +return \ No newline at end of file diff --git a/examples/calculator/contract.json b/examples/calculator/contract.json new file mode 100644 index 000000000..4b23fa17e --- /dev/null +++ b/examples/calculator/contract.json @@ -0,0 +1,74 @@ +{ + "name": "Calculator", + "methods": [ + { + "name": "add", + "args": [ + { + "type": "uint64", + "name": "a" + }, + { + "type": "uint64", + "name": "b" + } + ], + "returns": { + "type": "uint64" + }, + "desc": "Add a and b, return the result" + }, + { + "name": "mul", + "args": [ + { + "type": "uint64", + "name": "a" + }, + { + "type": "uint64", + "name": "b" + } + ], + "returns": { + "type": "uint64" + }, + "desc": "Multiply a and b, return the result" + }, + { + "name": "sub", + "args": [ + { + "type": "uint64", + "name": "a" + }, + { + "type": "uint64", + "name": "b" + } + ], + "returns": { + "type": "uint64" + }, + "desc": "Subtract b from a, return the result" + }, + { + "name": "div", + "args": [ + { + "type": "uint64", + "name": "a" + }, + { + "type": "uint64", + "name": "b" + } + ], + "returns": { + "type": "uint64" + }, + "desc": "Divide a by b, return the result" + } + ], + "networks": {} +} \ No newline at end of file diff --git a/examples/lsig/sample_arg.teal b/examples/lsig/sample_arg.teal new file mode 100644 index 000000000..8f21008e4 --- /dev/null +++ b/examples/lsig/sample_arg.teal @@ -0,0 +1,5 @@ +#pragma version 5 +arg_0 +btoi +int 123 +== \ No newline at end of file diff --git a/examples/lsig/simple.teal b/examples/lsig/simple.teal new file mode 100644 index 000000000..d62986563 --- /dev/null +++ b/examples/lsig/simple.teal @@ -0,0 +1,3 @@ +#pragma version 5 +int 1 +return \ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml new file mode 100644 index 000000000..3b06f5d04 --- /dev/null +++ b/examples/pom.xml @@ -0,0 +1,119 @@ + + 4.0.0 + com.algorand + sdk-extras + jar + 1.0-SNAPSHOT + sdk-extras + http://maven.apache.org + + + 1.8 + 1.8 + + + + + com.fasterxml.jackson.core + jackson-annotations + 2.10.0 + + + com.fasterxml.jackson.core + jackson-core + 2.10.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.10.5.1 + + + com.squareup.okhttp + logging-interceptor + 2.7.5 + + + com.squareup.okhttp + okhttp + 2.7.5 + + + com.squareup.okio + okio + 1.6.0 + + + commons-codec + commons-codec + + 1.12 + + + io.gsonfire + gson-fire + 1.8.0 + + + io.swagger + swagger-annotations + 1.5.18 + + + org.apache.commons + commons-lang3 + + 3.8 + + + org.bouncycastle + bcprov-jdk15to18 + 1.66 + + + org.msgpack + jackson-dataformat-msgpack + 0.9.0 + + + org.threeten + threetenbp + 1.3.5 + + + com.google.guava + guava + 28.2-android + + + junit + junit + 3.8.1 + test + + + com.algorand + algosdk + 2.0.0 + + + + + + + maven-assembly-plugin + + + + com.algorand.examples.Example + + + + jar-with-dependencies + + + + + + diff --git a/examples/runit.sh b/examples/runit.sh new file mode 100755 index 000000000..2b3eab9f4 --- /dev/null +++ b/examples/runit.sh @@ -0,0 +1,2 @@ +mvn clean compile assembly:single +java -cp target/sdk-extras-1.0-SNAPSHOT-jar-with-dependencies.jar com.algorand.examples.Example diff --git a/examples/src/main/java/com/algorand/examples/ASAExamples.java b/examples/src/main/java/com/algorand/examples/ASAExamples.java new file mode 100644 index 000000000..e68389743 --- /dev/null +++ b/examples/src/main/java/com/algorand/examples/ASAExamples.java @@ -0,0 +1,198 @@ +package com.algorand.examples; + +import java.util.List; + +import org.bouncycastle.pqc.crypto.ExchangePair; + +import com.algorand.algosdk.account.Account; +import com.algorand.algosdk.transaction.SignedTransaction; +import com.algorand.algosdk.transaction.Transaction; +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.Utils; +import com.algorand.algosdk.v2.client.common.AlgodClient; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.Asset; +import com.algorand.algosdk.v2.client.model.PendingTransactionResponse; +import com.algorand.algosdk.v2.client.model.PostTransactionsResponse; +import com.algorand.algosdk.v2.client.model.TransactionParametersResponse; + +public class ASAExamples { + + public static void main(String[] args) throws Exception { + AlgodClient algodClient = ExampleUtils.getAlgodClient(); + List accts = ExampleUtils.getSandboxAccounts(); + Account acct1 = accts.get(0); + Account acct2 = accts.get(1); + + Long asaId = createAsset(algodClient, acct1); + + printAssetInfo(algodClient, asaId); + configureAsset(algodClient, acct1, asaId); + optInToAsset(algodClient, acct2, asaId); + xferAsset(algodClient, acct1, acct2, asaId); + freezeAsset(algodClient, acct1, acct2, asaId); + clawbackAsset(algodClient, acct1, acct2, asaId); + deleteAsset(algodClient, acct1, asaId); + } + + public static void printAssetInfo(AlgodClient algodClient, Long asaId) throws Exception { + // example: ASSET_INFO + // Retrieve the asset info of the newly created asset + Response assetResp = algodClient.GetAssetByID(asaId).execute(); + Asset assetInfo = assetResp.body(); + System.out.printf("Asset Name: %s\n", assetInfo.params.name); + // example: ASSET_INFO + } + + public static Long createAsset(AlgodClient algodClient, Account acct) throws Exception { + // example: ASSET_CREATE + // Account 1 creates an asset called `rug` with a total supply + // of 1000 units and sets itself to the freeze/clawback/manager/reserve roles + Response rsp = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = rsp.body(); + + // Under the covers, this is an AssetConfig with asset id set to 0 + Transaction createTxn = Transaction.AssetCreateTransactionBuilder().suggestedParams(sp) + .sender(acct.getAddress()) + .assetTotal(1000) + .assetDecimals(0) + .defaultFrozen(false) + .assetUnitName("rug") + .assetName("Really Useful Gift") + .url("https://path/to/my/asset/details") + .manager(acct.getAddress()) + .reserve(acct.getAddress()) + .freeze(acct.getAddress()) + .clawback(acct.getAddress()) + .build(); + + SignedTransaction signedCreateTxn = acct.signTransaction(createTxn); + Response submitResult = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(signedCreateTxn)).execute(); + String txId = submitResult.body().txId; + PendingTransactionResponse result = Utils.waitForConfirmation(algodClient, txId, 4); + + // Grab the asset id for the asset we just created + Long asaId = result.assetIndex; + System.out.printf("Created asset with id: %d\n", asaId); + + // example: ASSET_CREATE + return asaId; + } + + public static void configureAsset(AlgodClient algodClient, Account acct, Long asaId) throws Exception { + // example: ASSET_CONFIG + Response rsp = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = rsp.body(); + // Wipe the `reserve` address through an AssetConfigTransaction + Transaction reconfigureTxn = Transaction.AssetConfigureTransactionBuilder().suggestedParams(sp) + .sender(acct.getAddress()) + .assetIndex(asaId) + .manager(acct.getAddress()) + .freeze(acct.getAddress()) + .clawback(acct.getAddress()) + .strictEmptyAddressChecking(false) + .reserve(new byte[32]) + .build(); + + // example: ASSET_CONFIG + SignedTransaction signedReconfigure = acct.signTransaction(reconfigureTxn); + ExampleUtils.sendPrint(algodClient, signedReconfigure, "config asset"); + } + + public static void optInToAsset(AlgodClient algodClient, Account acct, Long asaId) throws Exception { + // example: ASSET_OPTIN + Response rsp = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = rsp.body(); + // Under the covers, this is an AssetTransfer from me to me for amount 0 + // with asset id set to the asset we wish to start accepting + Transaction optInTxn = Transaction.AssetAcceptTransactionBuilder().suggestedParams(sp) + .sender(acct.getAddress()) + .assetIndex(asaId) + .build(); + + // example: ASSET_OPTIN + SignedTransaction signedOptIn = acct.signTransaction(optInTxn); + ExampleUtils.sendPrint(algodClient, signedOptIn, "opt in asset"); + } + + public static void xferAsset(AlgodClient algodClient, Account sender, Account receiver, Long asaId) + throws Exception { + // example: ASSET_XFER + Response rsp = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = rsp.body(); + // Under the covers, this is an AssetTransfer from me to me for amount 0 + // with asset id set to the asset we wish to start accepting + Transaction xferTxn = Transaction.AssetTransferTransactionBuilder().suggestedParams(sp) + .sender(sender.getAddress()) + .assetReceiver(receiver.getAddress()) + .assetIndex(asaId) + .assetAmount(1) + .build(); + + // example: ASSET_XFER + SignedTransaction signedXfer = sender.signTransaction(xferTxn); + ExampleUtils.sendPrint(algodClient, signedXfer, "xfer asset"); + } + + public static void freezeAsset(AlgodClient algodClient, Account sender, Account receiver, Long asaId) + throws Exception { + // example: ASSET_FREEZE + Response rsp = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = rsp.body(); + // Set the freeze state on the account, only the account that is set to the + // freeze role + // on the asset may issue this transaction + Transaction freezeTxn = Transaction.AssetFreezeTransactionBuilder().suggestedParams(sp) + .sender(sender.getAddress()) + .freezeTarget(receiver.getAddress()) + .freezeState(true) + .assetIndex(asaId) + .build(); + + // example: ASSET_FREEZE + SignedTransaction signedFreeze = sender.signTransaction(freezeTxn); + ExampleUtils.sendPrint(algodClient, signedFreeze, "freeze asset"); + } + + public static void clawbackAsset(AlgodClient algodClient, Account sender, Account receiver, Long asaId) + throws Exception { + // example: ASSET_CLAWBACK + Response rsp = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = rsp.body(); + // revoke an asset from an account, only the account that is set to the clawback + // role + // on the asset may issue this transaction + Transaction clawbackTxn = Transaction.AssetClawbackTransactionBuilder().suggestedParams(sp) + .sender(sender.getAddress()) + .assetClawbackFrom(receiver.getAddress()) + .assetReceiver(sender.getAddress()) + .assetIndex(asaId) + .assetAmount(1) + .build(); + + // example: ASSET_CLAWBACK + SignedTransaction signedClawback = sender.signTransaction(clawbackTxn); + ExampleUtils.sendPrint(algodClient, signedClawback, "clawback asset"); + } + + public static void deleteAsset(AlgodClient algodClient, Account acct, Long asaId) throws Exception { + // example: ASSET_DELETE + Response rsp = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = rsp.body(); + // Under the covers, an AssetDestroyTransaction is an AssetConfig with all of + // its + // configurable fields set to empty + // All units of the asset _must_ be owned by the creator account and this + // transaction _must_ + // be issued by the account set to the manager role on the asset + Transaction destroyTxn = Transaction.AssetDestroyTransactionBuilder().suggestedParams(sp) + .sender(acct.getAddress()) + .assetIndex(asaId) + .build(); + + // example: ASSET_DELETE + SignedTransaction signedDestroy = acct.signTransaction(destroyTxn); + ExampleUtils.sendPrint(algodClient, signedDestroy, "clawback asset"); + } +} diff --git a/examples/src/main/java/com/algorand/examples/ATC.java b/examples/src/main/java/com/algorand/examples/ATC.java new file mode 100644 index 000000000..a9423c0c3 --- /dev/null +++ b/examples/src/main/java/com/algorand/examples/ATC.java @@ -0,0 +1,184 @@ +package com.algorand.examples; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.codec.binary.Hex; + +import com.algorand.algosdk.abi.Contract; +import com.algorand.algosdk.abi.Method; +import com.algorand.algosdk.account.Account; +import com.algorand.algosdk.builder.transaction.ApplicationCreateTransactionBuilder; +import com.algorand.algosdk.builder.transaction.MethodCallTransactionBuilder; +import com.algorand.algosdk.builder.transaction.PaymentTransactionBuilder; +import com.algorand.algosdk.crypto.TEALProgram; +import com.algorand.algosdk.logic.StateSchema; +import com.algorand.algosdk.transaction.AppBoxReference; +import com.algorand.algosdk.transaction.AtomicTransactionComposer; +import com.algorand.algosdk.transaction.MethodCallParams; +import com.algorand.algosdk.transaction.SignedTransaction; +import com.algorand.algosdk.transaction.Transaction; +import com.algorand.algosdk.transaction.TransactionWithSigner; +import com.algorand.algosdk.transaction.AtomicTransactionComposer.ExecuteResult; +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.Utils; +import com.algorand.algosdk.v2.client.common.AlgodClient; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.CompileResponse; +import com.algorand.algosdk.v2.client.model.PendingTransactionResponse; +import com.algorand.algosdk.v2.client.model.PostTransactionsResponse; +import com.algorand.algosdk.v2.client.model.TransactionParametersResponse; + +public class ATC { + + public static void main(String[] args) throws Exception { + AlgodClient algodClient = ExampleUtils.getAlgodClient(); + List accts = ExampleUtils.getSandboxAccounts(); + Account acct = accts.get(0); + + Long appId = deployApp(algodClient, acct); + + // Get suggested params from client + Response rsp = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = rsp.body(); + + // example: ATC_CREATE + AtomicTransactionComposer atc = new AtomicTransactionComposer(); + // example: ATC_CREATE + + // example: ATC_ADD_TRANSACTION + // Create a transaction + Transaction ptxn = PaymentTransactionBuilder.Builder().amount(10000).suggestedParams(sp) + .sender(acct.getAddress()).receiver(acct.getAddress()).build(); + + // Construct TransactionWithSigner + TransactionWithSigner tws = new TransactionWithSigner(ptxn, + acct.getTransactionSigner()); + + // Pass TransactionWithSigner to atc + atc.addTransaction(tws); + // example: ATC_ADD_TRANSACTION + + // example: ATC_CONTRACT_INIT + // Read the json from disk + String jsonContract = Files.readString(Paths.get("calculator/contract.json")); + // Create Contract from Json + Contract contract = Encoder.decodeFromJson(jsonContract, Contract.class); + // example: ATC_CONTRACT_INIT + + // example: ATC_ADD_METHOD_CALL + // create methodCallParams by builder (or create by constructor) for add method + List methodArgs = new ArrayList(); + methodArgs.add(1); + methodArgs.add(1); + + MethodCallTransactionBuilder mctb = MethodCallTransactionBuilder.Builder(); + + MethodCallParams mcp = mctb.applicationId(appId).signer(acct.getTransactionSigner()) + .sender(acct.getAddress()) + .method(contract.getMethodByName("add")).methodArguments(methodArgs) + .onComplete(Transaction.OnCompletion.NoOpOC).suggestedParams(sp).build(); + + atc.addMethodCall(mcp); + // example: ATC_ADD_METHOD_CALL + + // example: ATC_RESULTS + ExecuteResult res = atc.execute(algodClient, 2); + System.out.printf("App call (%s) confirmed in round %d\n", res.txIDs, res.confirmedRound); + res.methodResults.forEach(methodResult -> { + System.out.printf("Result from calling '%s' method: %s\n", methodResult.method.name, + methodResult.value); + }); + // example: ATC_RESULTS + + // example: ATC_BOX_REF + MethodCallTransactionBuilder mct_builder = MethodCallTransactionBuilder.Builder(); + + List boxRefs = new ArrayList<>(); + boxRefs.add(new AppBoxReference(appId.intValue(), "cool-box".getBytes())); + MethodCallParams box_ref_mcp = mct_builder + .suggestedParams(sp) + .applicationId(appId) + .sender(acct.getAddress()) + .method(contract.getMethodByName("add")) + .methodArguments(methodArgs) + .signer(acct.getTransactionSigner()) + .onComplete(Transaction.OnCompletion.NoOpOC) + // Include reference to a box so the app logic may + // use it during evaluation + .boxReferences(boxRefs) + .build(); + // example: ATC_BOX_REF + } + + public static void atcWithTws(AlgodClient algodClient, Account account) throws Exception { + Response rsp = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = rsp.body(); + + Transaction ptxn = PaymentTransactionBuilder.Builder().amount(10000).suggestedParams(sp) + .sender(account.getAddress()).receiver(account.getAddress()).build(); + + // Construct TransactionWithSigner + TransactionWithSigner tws = new TransactionWithSigner(ptxn, + account.getTransactionSigner()); + + MethodCallTransactionBuilder mctb = MethodCallTransactionBuilder.Builder(); + + Method m = new Method("doit(pay,bool)void"); + + List methodArgs = new ArrayList<>(); + Boolean isHeads = true; + methodArgs.add(tws); + methodArgs.add(isHeads); + MethodCallParams mcp = mctb.applicationId(123l).signer(account.getTransactionSigner()) + .sender(account.getAddress()) + .method(m).methodArguments(methodArgs) + .onComplete(Transaction.OnCompletion.NoOpOC).suggestedParams(sp).build(); + + AtomicTransactionComposer atc = new AtomicTransactionComposer(); + atc.addMethodCall(mcp); + + List stxns = atc.gatherSignatures(); + System.out.printf("Created %d transactions\n", stxns.size()); + // should be the app call + SignedTransaction stxn = stxns.get(1); + List args = stxn.tx.applicationArgs; + for (int i = 0; i < args.size(); i++) { + System.out.printf("Arg %d is %s\n", i, Hex.encodeHexString(args.get(i))); + } + } + + public static Long deployApp(AlgodClient algodClient, Account acct1) throws Exception { + String approvalSource = Files.readString(Paths.get("calculator/approval.teal")); + String clearSource = Files.readString(Paths.get("calculator/clear.teal")); + + CompileResponse approvalResponse = algodClient.TealCompile().source(approvalSource.getBytes()).execute() + .body(); + CompileResponse clearResponse = algodClient.TealCompile().source(clearSource.getBytes()).execute() + .body(); + + TEALProgram approvalProg = new TEALProgram(approvalResponse.result); + TEALProgram clearProg = new TEALProgram(clearResponse.result); + + Response rsp = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = rsp.body(); + + StateSchema schema = new StateSchema(0, 0); + + Transaction appCreate = ApplicationCreateTransactionBuilder.Builder().sender(acct1.getAddress()) + .approvalProgram(approvalProg).clearStateProgram(clearProg).localStateSchema(schema) + .globalStateSchema(schema) + .suggestedParams(sp).build(); + SignedTransaction signedAppCreate = acct1.signTransaction(appCreate); + + Response createResult = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(signedAppCreate)).execute(); + + PendingTransactionResponse result = Utils.waitForConfirmation(algodClient, createResult.body().txId, 4); + + return result.applicationIndex; + } + +} diff --git a/examples/src/main/java/com/algorand/examples/AccountExamples.java b/examples/src/main/java/com/algorand/examples/AccountExamples.java new file mode 100644 index 000000000..84d21d1c2 --- /dev/null +++ b/examples/src/main/java/com/algorand/examples/AccountExamples.java @@ -0,0 +1,118 @@ +package com.algorand.examples; + +import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; + +import com.algorand.algosdk.account.Account; +import com.algorand.algosdk.crypto.Ed25519PublicKey; +import com.algorand.algosdk.crypto.MultisigAddress; +import com.algorand.algosdk.mnemonic.Mnemonic; +import com.algorand.algosdk.transaction.SignedTransaction; +import com.algorand.algosdk.transaction.Transaction; +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.common.AlgodClient; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.PostTransactionsResponse; +import com.algorand.algosdk.v2.client.model.TransactionParametersResponse; + +public class AccountExamples { + + public static void main(String[] args) throws Exception { + AlgodClient algodClient = ExampleUtils.getAlgodClient(); + List accts = ExampleUtils.getSandboxAccounts(); + + // Grab some accounts from the sandbox kmd + Account acct1 = accts.get(0); + Account acct2 = accts.get(1); + Account acct3 = accts.get(2); + + MultisigAddress msig = createMsig(acct1, acct2, acct3); + + // Pay the multisig address so it can issue transactions for the demo + Response spResponse = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = spResponse.body(); + Transaction ptxn = Transaction.PaymentTransactionBuilder() + .sender(acct1.getAddress()).amount(1000000).receiver(msig.toAddress()) + .suggestedParams(sp).build(); + SignedTransaction stxn = acct1.signTransaction(ptxn); + ExampleUtils.sendPrint(algodClient, stxn, "seed msig"); + + // example: MULTISIG_SIGN + // Construct transaction with sender as address of msig + Transaction msigPayTxn = Transaction.PaymentTransactionBuilder() + .sender(msig.toAddress()) + .amount(1000) + .receiver(acct1.getAddress()) + .suggestedParams(sp) + .build(); + + // For each subsig, sign or append to the existing partially signed transaction + SignedTransaction signedMsigPayTxn = acct1.signMultisigTransaction(msig, msigPayTxn); + signedMsigPayTxn = acct2.appendMultisigTransaction(msig, signedMsigPayTxn); + Response msigSubResponse = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(signedMsigPayTxn)).execute(); + // example: MULTISIG_SIGN + ExampleUtils.printTxnResults(algodClient, msigSubResponse.body(), "msig pay"); + + rekeyAcct(algodClient, acct1, acct2); + + } + public static Account recoverFromMnemonic() throws GeneralSecurityException { + // example: ACCOUNT_RECOVER_MNEMONIC + // Space delimited 25 word mnemonic + String mn = "cost piano sample enough south bar diet garden nasty mystery mesh sadness convince bacon best patch surround protect drum actress entire vacuum begin abandon hair"; + // We can get the private key + byte[] key = Mnemonic.toKey(mn); + // Or just init the account directly from the mnemonic + Account acct = new Account(mn); + // example: ACCOUNT_RECOVER_MNEMONIC + return acct; + } + + public static MultisigAddress createMsig(Account addr1, Account addr2, Account addr3) + throws NoSuchAlgorithmException { + // example: MULTISIG_CREATE + int version = 1; // no other versions at the time of writing + int threshold = 2; // we're making a 2/3 msig + + // Populate a list of Ed25519 pubkeys + List accts = new ArrayList<>(); + accts.add(addr1.getEd25519PublicKey()); + accts.add(addr2.getEd25519PublicKey()); + accts.add(addr3.getEd25519PublicKey()); + // create the MultisigAddress object + MultisigAddress msig = new MultisigAddress(version, threshold, accts); + System.out.printf("msig address: %s\n", msig.toAddress().toString()); + // example: MULTISIG_CREATE + return msig; + } + + public static void rekeyAcct(AlgodClient algodClient, Account acct1, Account acct2) throws Exception { + TransactionParametersResponse sp = algodClient.TransactionParams().execute().body(); + + // example: ACCOUNT_REKEY + + // Any kind of transaction can contain a rekey, here we use a Payment + // transaction + Transaction rekeyTxn = Transaction.PaymentTransactionBuilder().sender(acct1.getAddress()) + .receiver(acct1.getAddress()).suggestedParams(sp).rekey(acct2.getAddress()).build(); + SignedTransaction signedRekeyTxn = acct1.signTransaction(rekeyTxn); + Response resp = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(signedRekeyTxn)).execute(); + ExampleUtils.printTxnResults(algodClient, resp.body(), "rekey"); + + // Create a transaction to rekey it back + Transaction rekeyBack = Transaction.PaymentTransactionBuilder().sender(acct1.getAddress()) + .receiver(acct1.getAddress()).suggestedParams(sp).rekey(acct1.getAddress()).build(); + + // note we sign with acct2's key + SignedTransaction signedRekeyBack = acct2.signTransaction(rekeyBack); + Response rekeyBackResponse = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(signedRekeyBack)).execute(); + ExampleUtils.printTxnResults(algodClient, rekeyBackResponse.body(), "rekey back"); + // example: ACCOUNT_REKEY + } + +} \ No newline at end of file diff --git a/examples/src/main/java/com/algorand/examples/AppExamples.java b/examples/src/main/java/com/algorand/examples/AppExamples.java new file mode 100644 index 000000000..811b3676e --- /dev/null +++ b/examples/src/main/java/com/algorand/examples/AppExamples.java @@ -0,0 +1,215 @@ +package com.algorand.examples; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.algorand.algosdk.account.Account; +import com.algorand.algosdk.builder.transaction.ApplicationCallTransactionBuilder; +import com.algorand.algosdk.builder.transaction.ApplicationClearTransactionBuilder; +import com.algorand.algosdk.builder.transaction.ApplicationCloseTransactionBuilder; +import com.algorand.algosdk.builder.transaction.ApplicationCreateTransactionBuilder; +import com.algorand.algosdk.builder.transaction.ApplicationDeleteTransactionBuilder; +import com.algorand.algosdk.builder.transaction.ApplicationOptInTransactionBuilder; +import com.algorand.algosdk.builder.transaction.ApplicationUpdateTransactionBuilder; +import com.algorand.algosdk.crypto.TEALProgram; +import com.algorand.algosdk.logic.StateSchema; +import com.algorand.algosdk.transaction.SignedTransaction; +import com.algorand.algosdk.transaction.Transaction; +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.Utils; +import com.algorand.algosdk.v2.client.common.AlgodClient; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.CompileResponse; +import com.algorand.algosdk.v2.client.model.PendingTransactionResponse; +import com.algorand.algosdk.v2.client.model.PostTransactionsResponse; +import com.algorand.algosdk.v2.client.model.TransactionParametersResponse; + +public class AppExamples { + + public static void main(String[] args) throws Exception { + AlgodClient algodClient = ExampleUtils.getAlgodClient(); + List accts = ExampleUtils.getSandboxAccounts(); + + Account creator = accts.get(0); + Account user = accts.get(1); + + // example: APP_SCHEMA + int localInts = 1; + int localBytes = 1; + int globalInts = 1; + int globalBytes = 0; + StateSchema localSchema = new StateSchema(localInts, localBytes); + StateSchema globalSchema = new StateSchema(globalInts, globalBytes); + // example: APP_SCHEMA + + // example: APP_SOURCE + // Read in the `teal` source files as a string + String approvalSource = Files.readString(Paths.get("application/approval.teal")); + String clearSource = Files.readString(Paths.get("application/clear.teal")); + // example: APP_SOURCE + + // example: APP_COMPILE + CompileResponse approvalResponse = algodClient.TealCompile().source(approvalSource.getBytes()).execute() + .body(); + CompileResponse clearResponse = algodClient.TealCompile().source(clearSource.getBytes()).execute() + .body(); + + TEALProgram approvalProg = new TEALProgram(approvalResponse.result); + TEALProgram clearProg = new TEALProgram(clearResponse.result); + // example: APP_COMPILE + + // example: APP_CREATE + Response rsp = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = rsp.body(); + + Transaction appCreate = ApplicationCreateTransactionBuilder.Builder() + .sender(creator.getAddress()) + .suggestedParams(sp) + .approvalProgram(approvalProg) + .clearStateProgram(clearProg) + .localStateSchema(localSchema) + .globalStateSchema(globalSchema) + .build(); + + SignedTransaction signedAppCreate = creator.signTransaction(appCreate); + Response createResponse = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(signedAppCreate)).execute(); + + PendingTransactionResponse result = Utils.waitForConfirmation(algodClient, createResponse.body().txId, 4); + Long appId = result.applicationIndex; + System.out.printf("Created application with id: %d\n", appId); + // example: APP_CREATE + + // example: APP_OPTIN + Transaction optInTxn = ApplicationOptInTransactionBuilder.Builder() + .sender(user.getAddress()) + .suggestedParams(sp) + .applicationId(appId) + .build(); + + SignedTransaction signedOptIn = user.signTransaction(optInTxn); + Response optInResponse = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(signedOptIn)).execute(); + + PendingTransactionResponse optInResult = Utils.waitForConfirmation(algodClient, optInResponse.body().txId, 4); + assert optInResult.confirmedRound > 0; + // example: APP_OPTIN + + // example: APP_NOOP + Transaction noopTxn = ApplicationCallTransactionBuilder.Builder() + .sender(user.getAddress()) + .suggestedParams(sp) + .applicationId(appId) + .build(); + + SignedTransaction signedNoop = user.signTransaction(noopTxn); + Response noopResponse = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(signedNoop)).execute(); + + PendingTransactionResponse noopResult = Utils.waitForConfirmation(algodClient, noopResponse.body().txId, 4); + assert noopResult.confirmedRound > 0; + // example: APP_NOOP + + // example: APP_READ_STATE + // example: APP_READ_STATE + + // example: APP_UPDATE + String approvalSourceUpdated = Files.readString(Paths.get("application/approval_refactored.teal")); + CompileResponse approvalUpdatedResponse = algodClient.TealCompile().source(approvalSourceUpdated.getBytes()) + .execute() + .body(); + TEALProgram approvalProgUpdated = new TEALProgram(approvalUpdatedResponse.result); + + Transaction appUpdate = ApplicationUpdateTransactionBuilder.Builder() + .sender(creator.getAddress()) + .suggestedParams(sp) + .applicationId(appId) + .approvalProgram(approvalProgUpdated) + .clearStateProgram(clearProg) + .build(); + + SignedTransaction signedAppUpdate = creator.signTransaction(appUpdate); + Response updateResponse = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(signedAppUpdate)).execute(); + PendingTransactionResponse updateResult = Utils.waitForConfirmation(algodClient, updateResponse.body().txId, 4); + assert updateResult.confirmedRound > 0; + // example: APP_UPDATE + + // example: APP_CALL + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss"); + Date date = new Date(System.currentTimeMillis()); + + List appArgs = new ArrayList(); + appArgs.add(formatter.format(date).toString().getBytes()); + + // create unsigned transaction + Transaction callTransaction = ApplicationCallTransactionBuilder.Builder() + .sender(user.getAddress()) + .suggestedParams(sp) + .applicationId(appId) + .args(appArgs) + .build(); + + SignedTransaction signedCallTransaction = user.signTransaction(callTransaction); + Response callResponse = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(signedCallTransaction)).execute(); + + PendingTransactionResponse callResult = Utils.waitForConfirmation(algodClient, callResponse.body().txId, 4); + assert callResult.confirmedRound > 0; + // display results + if (callResult.globalStateDelta != null) { + System.out.printf("\tGlobal state: %s\n", callResult.globalStateDelta); + } + + if (callResult.localStateDelta != null) { + System.out.printf("\tLocal state: %s\n", callResult.localStateDelta); + } + // example: APP_CALL + + // example: APP_CLOSEOUT + Transaction closeOutTxn = ApplicationCloseTransactionBuilder.Builder() + .sender(user.getAddress()) + .suggestedParams(sp) + .applicationId(appId) + .build(); + + SignedTransaction signedCloseOut = user.signTransaction(closeOutTxn); + Response closeOutResponse = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(signedCloseOut)).execute(); + + PendingTransactionResponse closeOutResult = Utils.waitForConfirmation(algodClient, closeOutResponse.body().txId, + 4); + assert closeOutResult.confirmedRound > 0; + // example: APP_CLOSEOUT + + // example: APP_DELETE + Transaction appDelete = ApplicationDeleteTransactionBuilder.Builder() + .sender(creator.getAddress()) + .suggestedParams(sp) + .applicationId(appId) + .build(); + + SignedTransaction signedAppDelete = creator.signTransaction(appDelete); + Response deleteResponse = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(signedAppDelete)).execute(); + PendingTransactionResponse deleteResult = Utils.waitForConfirmation(algodClient, deleteResponse.body().txId, 4); + assert deleteResult.confirmedRound > 0; + // example: APP_DELETE + + // example: APP_CLEAR + Transaction clearTxn = ApplicationClearTransactionBuilder.Builder() + .sender(user.getAddress()) + .suggestedParams(sp) + .applicationId(appId) + .build(); + + SignedTransaction signedClear = user.signTransaction(clearTxn); + // ... sign, send, wait + // example: APP_CLEAR + } + +} \ No newline at end of file diff --git a/examples/src/main/java/com/algorand/examples/AtomicTransfers.java b/examples/src/main/java/com/algorand/examples/AtomicTransfers.java new file mode 100644 index 000000000..95fc9c86b --- /dev/null +++ b/examples/src/main/java/com/algorand/examples/AtomicTransfers.java @@ -0,0 +1,76 @@ +package com.algorand.examples; + +import java.util.List; + +import com.algorand.algosdk.account.Account; +import com.algorand.algosdk.crypto.Digest; +import com.algorand.algosdk.transaction.SignedTransaction; +import com.algorand.algosdk.transaction.Transaction; +import com.algorand.algosdk.transaction.TxGroup; +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.Utils; +import com.algorand.algosdk.v2.client.common.AlgodClient; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.PendingTransactionResponse; +import com.algorand.algosdk.v2.client.model.PostTransactionsResponse; +import com.algorand.algosdk.v2.client.model.TransactionParametersResponse; + +public class AtomicTransfers { + public static void main(String[] args) throws Exception { + AlgodClient algodClient = ExampleUtils.getAlgodClient(); + List accts = ExampleUtils.getSandboxAccounts(); + + Account acct1 = accts.get(0); + Account acct2 = accts.get(1); + + // example: ATOMIC_CREATE_TXNS + Response rsp = algodClient.TransactionParams().execute(); + + // payment from account 1 to account 2 + Transaction ptxn1 = Transaction.PaymentTransactionBuilder().sender(acct1.getAddress()) + .amount(1000000).receiver(acct2.getAddress()).suggestedParams(rsp.body()).build(); + // txn_1 = transaction.PaymentTxn(addr1, suggested_params, addr2, 100000) + + // payment from account 2 to account 1 + Transaction ptxn2 = Transaction.PaymentTransactionBuilder().sender(acct2.getAddress()) + .amount(2000000).receiver(acct1.getAddress()).suggestedParams(rsp.body()).build(); + // example: ATOMIC_CREATE_TXNS + + // example: ATOMIC_GROUP_TXNS + // Assign group id to the transactions (order matters!) + Transaction[] txs = TxGroup.assignGroupID(ptxn1, ptxn2); + + // Or, equivalently + // compute group id and assign it to transactions + Digest gid = TxGroup.computeGroupID(txs); + ptxn1.group = gid; + ptxn2.group = gid; + // example: ATOMIC_GROUP_TXNS + + // example: ATOMIC_GROUP_SIGN + // sign transactions + SignedTransaction signedPtxn1 = acct1.signTransaction(ptxn1); + SignedTransaction signedPtxn2 = acct2.signTransaction(ptxn2); + // # example: ATOMIC_GROUP_SIGN + + // example: ATOMIC_GROUP_ASSEMBLE + // combine the signed transactions into a single list + SignedTransaction[] stxns = new SignedTransaction[] { signedPtxn1, signedPtxn2 }; + // example: ATOMIC_GROUP_ASSEMBLE + + // example: ATOMIC_GROUP_SEND + // Only the first transaction id is returned + Response txResponse = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(stxns)).execute(); + String txid = txResponse.body().txId; + + // Wait for the transaction id to be confirmed + // If the results from other transactions are needed, grab the txid from those + // directly and + // call waitForConfirmation on each + PendingTransactionResponse txResult = Utils.waitForConfirmation(algodClient, txid, 4); + System.out.printf("Transaction %s confirmed in round %d\n", txid, txResult.confirmedRound); + // example: ATOMIC_GROUP_SEND + } + +} \ No newline at end of file diff --git a/examples/src/main/java/com/algorand/examples/CodecExamples.java b/examples/src/main/java/com/algorand/examples/CodecExamples.java new file mode 100644 index 000000000..0507bd63b --- /dev/null +++ b/examples/src/main/java/com/algorand/examples/CodecExamples.java @@ -0,0 +1,82 @@ +package com.algorand.examples; + +import java.math.BigInteger; +import java.util.List; + +import org.apache.commons.codec.binary.Hex; + +import com.algorand.algosdk.abi.ABIType; +import com.algorand.algosdk.account.Account; +import com.algorand.algosdk.crypto.Address; +import com.algorand.algosdk.transaction.SignedTransaction; +import com.algorand.algosdk.transaction.Transaction; +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.common.AlgodClient; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.TransactionParametersResponse; + +public class CodecExamples { + + public static void main(String[] args) throws Exception { + AlgodClient algodClient = ExampleUtils.getAlgodClient(); + List accts = ExampleUtils.getSandboxAccounts(); + Account acct = accts.get(0); + + // example: CODEC_ADDRESS + String addrAsStr = "4H5UNRBJ2Q6JENAXQ6HNTGKLKINP4J4VTQBEPK5F3I6RDICMZBPGNH6KD4"; + // Instantiate a new Address object with string + Address addr = new Address(addrAsStr); + // Or with the bytes + Address addrAgain = new Address(addr.getBytes()); + assert addrAgain.equals(addr); + // example: CODEC_ADDRESS + + // example: CODEC_BASE64 + String encodedStr = "SGksIEknbSBkZWNvZGVkIGZyb20gYmFzZTY0"; + byte[] decodedBytes = Encoder.decodeFromBase64(encodedStr); + String reEncodedStr = Encoder.encodeToBase64(decodedBytes); + assert encodedStr.equals(reEncodedStr); + // example: CODEC_BASE64 + + // example: CODEC_UINT64 + BigInteger val = BigInteger.valueOf(1337); + byte[] encodedVal = Encoder.encodeUint64(val); + BigInteger decodedVal = Encoder.decodeUint64(encodedVal); + assert val.equals(decodedVal); + // example: CODEC_UINT64 + + // example: CODEC_TRANSACTION_UNSIGNED + Response rsp = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = rsp.body(); + // Wipe the `reserve` address through an AssetConfigTransaction + Transaction ptxn = Transaction.PaymentTransactionBuilder().suggestedParams(sp) + .sender(acct.getAddress()).receiver(acct.getAddress()).amount(100).build(); + + byte[] encodedTxn = Encoder.encodeToMsgPack(ptxn); + + Transaction decodedTxn = Encoder.decodeFromMsgPack(encodedTxn, Transaction.class); + assert decodedTxn.equals(ptxn); + // example: CODEC_TRANSACTION_UNSIGNED + + // example: CODEC_TRANSACTION_SIGNED + SignedTransaction signedTxn = acct.signTransaction(ptxn); + byte[] encodedSignedTxn = Encoder.encodeToMsgPack(signedTxn); + + SignedTransaction decodedSignedTransaction = Encoder.decodeFromMsgPack(encodedSignedTxn, + SignedTransaction.class); + assert decodedSignedTransaction.equals(signedTxn); + // example: CODEC_TRANSACTION_SIGNED + + // example: CODEC_ABI_TYPES + ABIType tupleCodec = ABIType.valueOf("(uint64,bool,string)"); + Object[] data = new Object[]{123, false, "hi"}; + byte[] encoded = tupleCodec.encode(data); + System.out.printf("Encoded: '%s'\n", Hex.encodeHexString(encoded)); + + ABIType boolCodec = ABIType.valueOf("bool"); + byte[] singleEncoded = boolCodec.encode(true); + System.out.printf("Single Encoded: '%s'\n", Hex.encodeHexString(singleEncoded)); + // example: CODEC_ABI_TYPES + + } +} diff --git a/examples/src/main/java/com/algorand/examples/Debug.java b/examples/src/main/java/com/algorand/examples/Debug.java new file mode 100644 index 000000000..d0a06d714 --- /dev/null +++ b/examples/src/main/java/com/algorand/examples/Debug.java @@ -0,0 +1,72 @@ +package com.algorand.examples; + +import java.io.FileOutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import com.algorand.algosdk.abi.Contract; +import com.algorand.algosdk.account.Account; +import com.algorand.algosdk.builder.transaction.MethodCallTransactionBuilder; +import com.algorand.algosdk.transaction.AtomicTransactionComposer; +import com.algorand.algosdk.transaction.MethodCallParams; +import com.algorand.algosdk.transaction.Transaction; +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.Utils; +import com.algorand.algosdk.v2.client.common.AlgodClient; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.DryrunRequest; +import com.algorand.algosdk.v2.client.model.DryrunResponse; +import com.algorand.algosdk.v2.client.model.DryrunTxnResult; +import com.algorand.algosdk.v2.client.model.TransactionParametersResponse; + +public class Debug { + + + public static void main(String[] args) throws Exception { + AlgodClient algodClient = ExampleUtils.getAlgodClient(); + List accts = ExampleUtils.getSandboxAccounts(); + Account acct = accts.get(0); + + Long appId = ATC.deployApp(algodClient, acct); + + String jsonContract = Files.readString(Paths.get("calculator/contract.json")); + Contract contract = Encoder.decodeFromJson(jsonContract, Contract.class); + + // example: DEBUG_DRYRUN_DUMP + // Set up the transactions we'd like to dryrun + AtomicTransactionComposer atc = new AtomicTransactionComposer(); + + List methodArgs = new ArrayList(); + methodArgs.add(1); + methodArgs.add(1); + + TransactionParametersResponse sp = algodClient.TransactionParams().execute().body(); + + MethodCallTransactionBuilder mctb = MethodCallTransactionBuilder.Builder(); + MethodCallParams mcp = mctb.applicationId(appId).signer(acct.getTransactionSigner()) + .suggestedParams(sp) + .sender(acct.getAddress()) + .method(contract.getMethodByName("add")) + .methodArguments(methodArgs) + .onComplete(Transaction.OnCompletion.NoOpOC) + .build(); + atc.addMethodCall(mcp); + + DryrunRequest drr = Utils.createDryrun(algodClient, atc.gatherSignatures(), "", 0L, 0L); + + FileOutputStream outfile = new FileOutputStream("my-dryrun.msgpack"); + outfile.write(Encoder.encodeToMsgPack(drr)); + outfile.close(); + // example: DEBUG_DRYRUN_DUMP + + // example: DEBUG_DRYRUN_SUBMIT + Response resp = algodClient.TealDryrun().request(drr).execute(); + DryrunResponse drResp = resp.body(); + DryrunTxnResult dryrunTxnResult = drResp.txns.get(0); + System.out.println(dryrunTxnResult.appCallMessages); + System.out.println(Utils.appTrace(dryrunTxnResult)); + // example: DEBUG_DRYRUN_SUBMIT + } +} diff --git a/examples/src/main/java/com/algorand/examples/ExampleUtils.java b/examples/src/main/java/com/algorand/examples/ExampleUtils.java new file mode 100644 index 000000000..07fc8c1e3 --- /dev/null +++ b/examples/src/main/java/com/algorand/examples/ExampleUtils.java @@ -0,0 +1,106 @@ +package com.algorand.examples; + +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; + +import com.algorand.algosdk.v2.client.Utils; +import com.algorand.algosdk.v2.client.common.AlgodClient; +import com.algorand.algosdk.v2.client.common.IndexerClient; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.PendingTransactionResponse; +import com.algorand.algosdk.v2.client.model.PostTransactionsResponse; +import com.algorand.algosdk.account.Account; +import com.algorand.algosdk.crypto.Address; +import com.algorand.algosdk.kmd.client.KmdClient; +import com.algorand.algosdk.kmd.client.api.KmdApi; +import com.algorand.algosdk.kmd.client.model.APIV1Wallet; +import com.algorand.algosdk.kmd.client.model.ExportKeyRequest; +import com.algorand.algosdk.kmd.client.model.InitWalletHandleTokenRequest; +import com.algorand.algosdk.kmd.client.model.ListKeysRequest; +import com.algorand.algosdk.transaction.SignedTransaction; +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.kmd.client.ApiException; + +public class ExampleUtils { + + private static String kmd_host = "http://localhost:4002"; + private static String kmd_token = "a".repeat(64); + private static KmdApi kmd = null; + + private static String algod_host = "http://localhost"; + private static int algod_port = 4001; + private static String algod_token = "a".repeat(64); + + private static String indexer_host = "http://localhost"; + private static int indexer_port = 8980; + private static String indexer_token = "a".repeat(64); + + public static AlgodClient getAlgodClient() { + return new AlgodClient(algod_host, algod_port, algod_token); + } + + public static IndexerClient getIndexerClient() { + return new IndexerClient(indexer_host, indexer_port, indexer_token); + } + + public static List getSandboxAccounts() throws Exception { + // Initialize KMD v1 client + KmdClient kmdClient = new KmdClient(); + kmdClient.setBasePath(kmd_host); + kmdClient.setApiKey(kmd_token); + kmd = new KmdApi(kmdClient); + + // Get accounts from sandbox. + String walletHandle = getDefaultWalletHandle(); + List
addresses = getWalletAccounts(walletHandle); + + List accts = new ArrayList<>(); + for (Address addr : addresses) { + byte[] pk = lookupPrivateKey(addr, walletHandle); + accts.add(new Account(pk)); + } + return accts; + } + + public static byte[] lookupPrivateKey(Address addr, String walletHandle) throws ApiException { + ExportKeyRequest req = new ExportKeyRequest(); + req.setAddress(addr.toString()); + req.setWalletHandleToken(walletHandle); + req.setWalletPassword(""); + return kmd.exportKey(req).getPrivateKey(); + } + + public static String getDefaultWalletHandle() throws ApiException { + for (APIV1Wallet w : kmd.listWallets().getWallets()) { + if (w.getName().equals("unencrypted-default-wallet")) { + InitWalletHandleTokenRequest tokenreq = new InitWalletHandleTokenRequest(); + tokenreq.setWalletId(w.getId()); + tokenreq.setWalletPassword(""); + return kmd.initWalletHandleToken(tokenreq).getWalletHandleToken(); + } + } + throw new RuntimeException("Default wallet not found."); + } + + public static List
getWalletAccounts(String walletHandle) throws ApiException, NoSuchAlgorithmException { + List
accounts = new ArrayList<>(); + ListKeysRequest keysRequest = new ListKeysRequest(); + keysRequest.setWalletHandleToken(walletHandle); + for (String addr : kmd.listKeysInWallet(keysRequest).getAddresses()) { + accounts.add(new Address(addr)); + } + return accounts; + } + + public static void sendPrint(AlgodClient algodClient, SignedTransaction stxn, String name) throws Exception { + Response submitResult = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(stxn)).execute(); + printTxnResults(algodClient, submitResult.body(), name); + } + + public static void printTxnResults(AlgodClient client, PostTransactionsResponse ptr, String name) throws Exception { + PendingTransactionResponse result = Utils.waitForConfirmation(client, ptr.txId, 4); + System.out.printf("%s\ttransaction (%s) was confirmed in round %d\n", name, ptr.txId, result.confirmedRound); + } +} \ No newline at end of file diff --git a/examples/src/main/java/com/algorand/examples/IndexerExamples.java b/examples/src/main/java/com/algorand/examples/IndexerExamples.java new file mode 100644 index 000000000..c269af75a --- /dev/null +++ b/examples/src/main/java/com/algorand/examples/IndexerExamples.java @@ -0,0 +1,57 @@ +package com.algorand.examples; + +import com.algorand.algosdk.v2.client.common.IndexerClient; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.Asset; +import com.algorand.algosdk.v2.client.model.AssetResponse; +import com.algorand.algosdk.v2.client.model.TransactionsResponse; + +public class IndexerExamples { + + public static void main(String[] args) throws Exception { + // example: CREATE_INDEXER_CLIENT + String indexerHost = "http://localhost"; + int indexerPort = 8980; + String indexerToken = "a".repeat(64); + IndexerClient indexerClient = new IndexerClient(indexerHost, indexerPort, indexerToken); + // example: CREATE_INDEXER_CLIENT + + // example: INDEXER_LOOKUP_ASSET + Long asaId = 25l; + Response assetResponse = indexerClient.lookupAssetByID(asaId).execute(); + Asset assetInfo = assetResponse.body().asset; + System.out.printf("Name for %d: %s\n", asaId, assetInfo.params.name); + // example: INDEXER_LOOKUP_ASSET + + // example: INDEXER_SEARCH_MIN_AMOUNT + Response transactionSearchResult = indexerClient.searchForTransactions() + .minRound(10l).maxRound(500l).currencyGreaterThan(10l).execute(); + TransactionsResponse txResp = transactionSearchResult.body(); + System.out.printf("Found %d transactions that match criteria\n", txResp.transactions.size()); + // example: INDEXER_SEARCH_MIN_AMOUNT + + // example: INDEXER_PAGINATE_RESULTS + String nextToken = ""; + boolean hasResults = true; + // Start with empty nextToken and while there are + // results in the transaction results, query again with the next page + while(hasResults){ + Response searchResults = indexerClient.searchForTransactions().minRound(1000l) + .maxRound(1500l).currencyGreaterThan(10l).next(nextToken).execute(); + TransactionsResponse txnRes = searchResults.body(); + // + // ... do something with transaction results + // + hasResults = txnRes.transactions.size()>0; + nextToken = txnRes.nextToken; + } + // example: INDEXER_PAGINATE_RESULTS + + // example: INDEXER_PREFIX_SEARCH + byte[] prefix = new String("showing prefix").getBytes(); + Response prefixResults = indexerClient.searchForTransactions().notePrefix(prefix).execute(); + // ... + // example: INDEXER_PREFIX_SEARCH + } + +} diff --git a/examples/src/main/java/com/algorand/examples/KMDExamples.java b/examples/src/main/java/com/algorand/examples/KMDExamples.java new file mode 100644 index 000000000..9f20115bb --- /dev/null +++ b/examples/src/main/java/com/algorand/examples/KMDExamples.java @@ -0,0 +1,120 @@ +package com.algorand.examples; + +import org.bouncycastle.util.Arrays; + +import com.algorand.algosdk.kmd.client.ApiException; +import com.algorand.algosdk.kmd.client.KmdClient; +import com.algorand.algosdk.kmd.client.api.KmdApi; +import com.algorand.algosdk.kmd.client.model.APIV1POSTKeyExportResponse; +import com.algorand.algosdk.kmd.client.model.APIV1POSTKeyImportResponse; +import com.algorand.algosdk.kmd.client.model.APIV1POSTKeyResponse; +import com.algorand.algosdk.kmd.client.model.APIV1POSTMasterKeyExportResponse; +import com.algorand.algosdk.kmd.client.model.APIV1POSTWalletInitResponse; +import com.algorand.algosdk.kmd.client.model.APIV1POSTWalletResponse; +import com.algorand.algosdk.kmd.client.model.APIV1Wallet; +import com.algorand.algosdk.kmd.client.model.CreateWalletRequest; +import com.algorand.algosdk.kmd.client.model.ExportKeyRequest; +import com.algorand.algosdk.kmd.client.model.ExportMasterKeyRequest; +import com.algorand.algosdk.kmd.client.model.GenerateKeyRequest; +import com.algorand.algosdk.kmd.client.model.ImportKeyRequest; +import com.algorand.algosdk.kmd.client.model.InitWalletHandleTokenRequest; +import com.algorand.algosdk.mnemonic.Mnemonic; + +public class KMDExamples { + + public static void main(String[] args) throws ApiException { + // example: KMD_CREATE_CLIENT + String kmdHost = "http://localhost:4002"; + String kmdToken = "a".repeat(64); + + KmdClient kmdClient = new KmdClient(); + kmdClient.setBasePath(kmdHost); + kmdClient.setApiKey(kmdToken); + + KmdApi kmd = new KmdApi(kmdClient); + // example: KMD_CREATE_CLIENT + + + String walletName = "MyNewWallet"; + String password = "supersecretpassword"; + + // example: KMD_CREATE_WALLET + // create a new CreateWalletRequest and set parameters + CreateWalletRequest cwr = new CreateWalletRequest(); + cwr.setWalletName(walletName); + cwr.setWalletPassword(password); + cwr.setWalletDriverName("sqlite"); // other option is `ledger` + // using our client, pass the request + APIV1POSTWalletResponse result = kmd.createWallet(cwr); + APIV1Wallet wallet = result.getWallet(); + System.out.printf("Wallet name: %s\n", wallet.getName()); + // example: KMD_CREATE_WALLET + + String handleToken = getHandle(kmd, wallet, password); + + // example: KMD_BACKUP_WALLET + ExportMasterKeyRequest mker = new ExportMasterKeyRequest(); + mker.setWalletHandleToken(handleToken); + mker.setWalletPassword(password); + APIV1POSTMasterKeyExportResponse masterKeyResp = kmd.exportMasterKey(mker); + byte[] backupKey = masterKeyResp.getMasterDerivationKey(); + // example: KMD_BACKUP_WALLET + + // example: KMD_RECOVER_WALLET + // create a new CreateWalletRequest and set parameters + CreateWalletRequest recoverRequest = new CreateWalletRequest(); + recoverRequest.setWalletName("Recovered:"+walletName); + recoverRequest.setWalletPassword(password); + recoverRequest.setWalletDriverName("sqlite"); + // Pass the specific derivation key we want to use + // to recover the wallet + recoverRequest.setMasterDerivationKey(backupKey); + APIV1POSTWalletResponse recoverResponse = kmd.createWallet(recoverRequest); + APIV1Wallet recoveredWallet = recoverResponse.getWallet(); + System.out.printf("Wallet name: %s\n", recoveredWallet.getName()); + // example: KMD_RECOVER_WALLET + + // example: KMD_CREATE_ACCOUNT + // create a request to generate a new key, using the handle token + GenerateKeyRequest gkr = new GenerateKeyRequest(); + gkr.setWalletHandleToken(handleToken); + APIV1POSTKeyResponse generatedKey = kmd.generateKey(gkr); + String addr = generatedKey.getAddress(); + System.out.printf("New account: %s\n", addr); + // example: KMD_CREATE_ACCOUNT + + // example: KMD_EXPORT_ACCOUNT + ExportKeyRequest ekr = new ExportKeyRequest(); + ekr.setAddress(addr); + ekr.setWalletHandleToken(handleToken); + ekr.setWalletPassword(password); + APIV1POSTKeyExportResponse exportedKeyResp = kmd.exportKey(ekr); + byte[] exportedKey = exportedKeyResp.getPrivateKey(); + String mn = Mnemonic.fromKey(Arrays.copyOfRange(exportedKey, 0, 32)); + System.out.printf("Exported mnemonic: %s\n", mn); + // example: KMD_EXPORT_ACCOUNT + + String recoveredWalletHandleToken = getHandle(kmd, recoveredWallet, password); + + // example: KMD_IMPORT_ACCOUNT + ImportKeyRequest ikr = new ImportKeyRequest(); + ikr.setPrivateKey(Arrays.copyOfRange(exportedKey, 0, 32)); + ikr.setWalletHandleToken(recoveredWalletHandleToken); + APIV1POSTKeyImportResponse importResp = kmd.importKey(ikr); + System.out.printf("Imported account: %s\n", importResp.getAddress()); + // /example: KMD_IMPORT_ACCOUNT + } + + public static String getHandle(KmdApi kmd, APIV1Wallet wallet, String password) throws ApiException { + // example: KMD_CREATE_HANDLE_TOKEN + // grab a handle that we can re-use + InitWalletHandleTokenRequest tokenReq = new InitWalletHandleTokenRequest(); + tokenReq.setWalletId(wallet.getId()); + tokenReq.setWalletPassword(password); + APIV1POSTWalletInitResponse handleTokenResp = kmd.initWalletHandleToken(tokenReq); + String handleToken = handleTokenResp.getWalletHandleToken(); + // example: KMD_CREATE_HANDLE_TOKEN + return handleToken; + } + +} diff --git a/examples/src/main/java/com/algorand/examples/LSig.java b/examples/src/main/java/com/algorand/examples/LSig.java new file mode 100644 index 000000000..924b8563c --- /dev/null +++ b/examples/src/main/java/com/algorand/examples/LSig.java @@ -0,0 +1,108 @@ +package com.algorand.examples; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import com.algorand.algosdk.account.Account; +import com.algorand.algosdk.account.LogicSigAccount; +import com.algorand.algosdk.crypto.LogicsigSignature; +import com.algorand.algosdk.transaction.SignedTransaction; +import com.algorand.algosdk.transaction.Transaction; +import com.algorand.algosdk.util.Encoder; +import com.algorand.algosdk.v2.client.Utils; +import com.algorand.algosdk.v2.client.common.AlgodClient; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.CompileResponse; +import com.algorand.algosdk.v2.client.model.PendingTransactionResponse; +import com.algorand.algosdk.v2.client.model.PostTransactionsResponse; +import com.algorand.algosdk.v2.client.model.TransactionParametersResponse; + +public class LSig { + + public static void main(String[] args) throws Exception { + AlgodClient algodClient = ExampleUtils.getAlgodClient(); + List accts = ExampleUtils.getSandboxAccounts(); + Account seedAcct = accts.get(0); + + byte[] tealBinary = compileLsig(algodClient, "lsig/simple.teal"); + // example: LSIG_INIT + LogicSigAccount lsig = new LogicSigAccount(tealBinary, null); + // example: LSIG_INIT + + byte[] tealBinaryWithArgs = compileLsig(algodClient, "lsig/simple.teal"); + // example: LSIG_PASS_ARGS + List tealArgs = new ArrayList(); + // The arguments _must_ be byte arrays + byte[] arg1 = Encoder.encodeUint64(123l); + tealArgs.add(arg1); + LogicSigAccount lsigWithArgs = new LogicSigAccount(tealBinaryWithArgs, tealArgs); + // example: LSIG_PASS_ARGS + + // Create a transaction to seed the lsig address + TransactionParametersResponse seedParams = algodClient.TransactionParams().execute().body(); + Transaction seedTxn = Transaction.PaymentTransactionBuilder() + .suggestedParams(seedParams) + .sender(seedAcct.getAddress()) + .amount(10000000) + .receiver(lsigWithArgs.getAddress()) + .build(); + ExampleUtils.sendPrint(algodClient, seedAcct.signTransaction(seedTxn), "seed lsig"); + + // example: LSIG_SIGN_FULL + TransactionParametersResponse params = algodClient.TransactionParams().execute().body(); + // create a transaction + Transaction txn = Transaction.PaymentTransactionBuilder() + .sender(lsig.getAddress()) + .amount(100000) + .receiver(seedAcct.getAddress()) + .suggestedParams(params) + .build(); + // create the LogicSigTransaction with contract account LogicSigAccount + SignedTransaction stx = Account.signLogicsigTransaction(lsig.lsig, txn); + // send raw LogicSigTransaction to network + Response submitResult = algodClient.RawTransaction() + .rawtxn(Encoder.encodeToMsgPack(stx)).execute(); + String txid = submitResult.body().txId; + // Wait for transaction confirmation + PendingTransactionResponse pTrx = Utils.waitForConfirmation(algodClient, txid, 4); + System.out.printf("Transaction %s confirmed in round %d\n", txid, pTrx.confirmedRound); + // example: LSIG_SIGN_FULL + + // example: LSIG_DELEGATE_FULL + // account signs the logic, and now the logic may be passed instead + // of a signature for a transaction + LogicsigSignature delegateLsig = seedAcct.signLogicsig(lsigWithArgs.lsig); + params = algodClient.TransactionParams().execute().body(); + // create a transaction where the sender is the signer of the lsig + txn = Transaction.PaymentTransactionBuilder() + .sender(seedAcct.getAddress()) + .amount(100000) + .receiver(delegateLsig.toAddress()) + .suggestedParams(params) + .build(); + // Sign the transaction with the delegate lsig + stx = Account.signLogicsigTransaction(delegateLsig, txn); + // send raw LogicSigTransaction to network + submitResult = algodClient.RawTransaction().rawtxn(Encoder.encodeToMsgPack(stx)).execute(); + txid = submitResult.body().txId; + // Wait for transaction confirmation + PendingTransactionResponse delegatResponse = Utils.waitForConfirmation(algodClient, txid, 4); + System.out.printf("Transaction %s confirmed in round %d\n", txid, delegatResponse.confirmedRound); + // example: LSIG_DELEGATE_FULL + } + + public static byte[] compileLsig(AlgodClient algodClient, String path) throws Exception { + // example: LSIG_COMPILE + String tealsrc = Files.readString(Paths.get("lsig/simple.teal")); + Response compileResp = algodClient.TealCompile().source(tealsrc.getBytes()).execute(); + System.out.printf("Program: %s\n", compileResp.body().result); + System.out.printf("Address: %s\n", compileResp.body().hash); + byte[] tealBinary = Encoder.decodeFromBase64(compileResp.body().result); + // example: LSIG_COMPILE + return tealBinary; + } + +} diff --git a/examples/src/main/java/com/algorand/examples/Overview.java b/examples/src/main/java/com/algorand/examples/Overview.java new file mode 100644 index 000000000..c2c72d2fb --- /dev/null +++ b/examples/src/main/java/com/algorand/examples/Overview.java @@ -0,0 +1,107 @@ +package com.algorand.examples; + +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import com.algorand.algosdk.v2.client.Utils; +import com.algorand.algosdk.v2.client.common.AlgodClient; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.PendingTransactionResponse; +import com.algorand.algosdk.v2.client.model.PostTransactionsResponse; +import com.algorand.algosdk.v2.client.model.TransactionParametersResponse; +import com.algorand.algosdk.account.Account; +import com.algorand.algosdk.transaction.SignedTransaction; +import com.algorand.algosdk.transaction.Transaction; +import com.algorand.algosdk.util.Encoder; + +public class Overview { + public static void main(String[] args) throws Exception { + AlgodClient algodClient = ExampleUtils.getAlgodClient(); + List accts = ExampleUtils.getSandboxAccounts(); + // grab the first one from the sandbox kmd + Account acct = accts.get(0); + Account acct2 = accts.get(1); + + // example: TRANSACTION_PAYMENT_CREATE + Response suggestedParams = algodClient.TransactionParams().execute(); + Integer amount = 1000000; // 1 Algo + Transaction ptxn = Transaction.PaymentTransactionBuilder() + .sender(acct.getAddress()) + .amount(amount) + .receiver(acct2.getAddress()) + .suggestedParams(suggestedParams.body()).build(); + // example: TRANSACTION_PAYMENT_CREATE + + // example: TRANSACTION_PAYMENT_SIGN + SignedTransaction sptxn = acct.signTransaction(ptxn); + // example: TRANSACTION_PAYMENT_SIGN + + // example: TRANSACTION_PAYMENT_SUBMIT + // encode the transaction + byte[] encodedTxBytes = Encoder.encodeToMsgPack(sptxn); + // submit the transaction to the algod server + Response resp = algodClient.RawTransaction().rawtxn(encodedTxBytes).execute(); + // wait for the transaction to be confirmed + String txid = resp.body().txId; + PendingTransactionResponse result = Utils.waitForConfirmation(algodClient, txid, 4); + System.out.printf("Transaction %s confirmed in round %d\n", txid, result.confirmedRound); + // example: TRANSACTION_PAYMENT_SUBMIT + + // example: SP_MIN_FEE + Response tpr = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = tpr.body(); + System.out.printf("Min fee from suggested params: %d\n", sp.minFee); + // example: SP_MIN_FEE + + // example: TRANSACTION_FEE_OVERRIDE + BigInteger nullFee = null; + Transaction feeOverrideTxn = Transaction.PaymentTransactionBuilder() + .sender(acct.getAddress()) + .receiver(acct2.getAddress()) + .suggestedParams(suggestedParams.body()) + // since suggestedParams sets a fee, we have to `null` it out + // or trying to set flatFee will fail with both set + .fee(nullFee) + // override the fee given by suggested params to set a flat + // fee of 2x minfee to cover another transaction in the same group + .flatFee(2 * suggestedParams.body().minFee).build(); + // example: TRANSACTION_FEE_OVERRIDE + + // example: CONST_MIN_FEE + System.out.printf("Min fee from const: %d\n", Account.MIN_TX_FEE_UALGOS); + // example: CONST_MIN_FEE + } + + public static void createAccount() throws NoSuchAlgorithmException { + // example: ACCOUNT_GENERATE + Account acct = new Account(); + System.out.println("Address: " + acct.getAddress()); + System.out.println("Passphrase: " + acct.toMnemonic()); + // example: ACCOUNT_GENERATE + } + + public static void getAccountInfo(AlgodClient algodClient, Account acct) throws Exception { + // example: ALGOD_FETCH_ACCOUNT_INFO + Response acctInfoResp = algodClient + .AccountInformation(acct.getAddress()).execute(); + com.algorand.algosdk.v2.client.model.Account acctInfo = acctInfoResp.body(); + // print one of the fields in the account info response + System.out.printf("Current balance: %d", acctInfo.amount); + // example: ALGOD_FETCH_ACCOUNT_INFO + } + + public static AlgodClient createClient() { + // example: ALGOD_CREATE_CLIENT + String algodHost = "http://localhost"; + int algodPort = 4001; + String algodToken = "a".repeat(64); + AlgodClient algodClient = new AlgodClient(algodHost, algodPort, algodToken); + + // OR if the API provider requires a specific header key for the token + String tokenHeader = "X-API-Key"; + AlgodClient otherAlgodClient = new AlgodClient(algodHost, algodPort, algodToken, tokenHeader); + // example: ALGOD_CREATE_CLIENT + return algodClient; + } + +} \ No newline at end of file diff --git a/examples/src/main/java/com/algorand/examples/Participation.java b/examples/src/main/java/com/algorand/examples/Participation.java new file mode 100644 index 000000000..27f0d4ebe --- /dev/null +++ b/examples/src/main/java/com/algorand/examples/Participation.java @@ -0,0 +1,46 @@ +package com.algorand.examples; + +import com.algorand.algosdk.transaction.Transaction; +import com.algorand.algosdk.v2.client.common.AlgodClient; +import com.algorand.algosdk.v2.client.common.Response; +import com.algorand.algosdk.v2.client.model.TransactionParametersResponse; + +public class Participation { + + public static void main(String[] args) throws Exception { + AlgodClient algodClient = ExampleUtils.getAlgodClient(); + + // example: TRANSACTION_KEYREG_ONLINE_CREATE + // get suggested parameters + Response rsp = algodClient.TransactionParams().execute(); + TransactionParametersResponse sp = rsp.body(); + + String address = "EW64GC6F24M7NDSC5R3ES4YUVE3ZXXNMARJHDCCCLIHZU6TBEOC7XRSBG4"; + + String votekey = "eXq34wzh2UIxCZaI1leALKyAvSz/+XOe0wqdHagM+bw="; + String skey = "X84ReKTmp+yfgmMCbbokVqeFFFrKQeFZKEXG89SXwm4="; + + Long numRounds = 100000l; // sets up keys for 100000 rounds + Long keyDilution = (long) Math.sqrt(numRounds); // dilution default is sqrt num rounds + + Transaction keyRegTxn = Transaction.KeyRegistrationTransactionBuilder().suggestedParams(sp) + .sender(address) + .selectionPublicKeyBase64(skey) + .participationPublicKeyBase64(votekey) + .voteFirst(sp.lastRound) + .voteLast(sp.lastRound + numRounds) + .voteKeyDilution(keyDilution) + .build(); + // ... sign and send to network + // example: TRANSACTION_KEYREG_ONLINE_CREATE + + // example: TRANSACTION_KEYREG_OFFLINE_CREATE + // create keyreg transaction to take this account offline + Transaction keyRegOfflineTxn = Transaction.KeyRegistrationTransactionBuilder().suggestedParams(sp) + .sender(address) + .build(); + // example: TRANSACTION_KEYREG_OFFLINE_CREATE + System.out.println(keyRegOfflineTxn); + } + +}