diff --git a/CHANGELOG.md b/CHANGELOG.md
index 84b00b442..3f7c1c7ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,8 @@
# Changelog
-## 12.1.0
-
+## Unreleased
+- Added `MintTokenOperation` implementing protocol level token mint
+- Added protocol level token mint example, see `MintToken` in `concordium-sdk-examples`
- Added deserialization for the `ConfigureDelegation` payload
- Fixed incorrect deserialization of the `UpdateContract` payload parameter
diff --git a/concordium-android-sdk/pom.xml b/concordium-android-sdk/pom.xml
index 24351a1a9..a1644335c 100644
--- a/concordium-android-sdk/pom.xml
+++ b/concordium-android-sdk/pom.xml
@@ -5,7 +5,7 @@
com.concordium.sdk
concordium-sdk-base
- 12.1.0-alpha.1
+ 12.1.0-alpha.2
concordium-android-sdk
diff --git a/concordium-sdk-examples/pom.xml b/concordium-sdk-examples/pom.xml
index 52858e30e..677ff5396 100644
--- a/concordium-sdk-examples/pom.xml
+++ b/concordium-sdk-examples/pom.xml
@@ -23,7 +23,7 @@
com.concordium.sdk
concordium-sdk
- 12.0.0
+ 12.1.0-alpha.2
org.projectlombok
diff --git a/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/MintToken.java b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/MintToken.java
new file mode 100644
index 000000000..ada895734
--- /dev/null
+++ b/concordium-sdk-examples/src/main/java/com/concordium/sdk/examples/MintToken.java
@@ -0,0 +1,95 @@
+package com.concordium.sdk.examples;
+
+import com.concordium.sdk.ClientV2;
+import com.concordium.sdk.Connection;
+import com.concordium.sdk.crypto.ed25519.ED25519SecretKey;
+import com.concordium.sdk.exceptions.ClientInitializationException;
+import com.concordium.sdk.requests.AccountQuery;
+import com.concordium.sdk.requests.BlockQuery;
+import com.concordium.sdk.responses.blockitemstatus.FinalizedBlockItem;
+import com.concordium.sdk.transactions.*;
+import com.concordium.sdk.transactions.tokens.TokenOperationAmount;
+import com.concordium.sdk.transactions.tokens.MintTokenOperation;
+import com.concordium.sdk.types.AccountAddress;
+import lombok.var;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+import java.math.BigDecimal;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+
+@Command(name = "MintToken", mixinStandardHelpOptions = true)
+public class MintToken implements Callable {
+ @Option(
+ names = {"--endpoint"},
+ description = "GRPC interface of the node.",
+ defaultValue = "https://127.0.0.1:7000")
+ private String endpoint;
+
+ @Option(
+ names = {"--timeout"},
+ description = "GRPC request timeout in milliseconds.",
+ defaultValue = "100000")
+ private int timeout;
+
+ @Override
+ public Integer call() throws MalformedURLException, ClientInitializationException {
+ var endpointUrl = new URL(this.endpoint);
+
+ Connection connection = Connection.newBuilder()
+ .host(endpointUrl.getHost())
+ .port(endpointUrl.getPort())
+ //.useTLS(TLSConfig.auto()) //update this as necessary if using tls or not
+ .build();
+
+ String tokenSymbol = "RRR10"; // Replace this with the appropriate token symbol in your node
+ TokenOperationAmount amount = new TokenOperationAmount(
+ new BigDecimal("1"),
+ 6 //ensure the decimal precision here matches the value of the decimal during token creation
+ );
+
+ //use an address valid in the node being used
+ AccountAddress sender = AccountAddress.from("4FbFhx5C5XaSUKdnTdrRVbtNW6rtgYYdKRJtggYGBSiktiymsX");
+ Expiry expiry = Expiry.createNew().addMinutes(5);
+
+ //replace with a valid sign key value from the accounts of the node being used
+ TransactionSigner signer = TransactionSigner.from(
+ SignerEntry.from(Index.from(0), Index.from(0),
+ ED25519SecretKey
+ .from("b4cef1510079ba946c093826d3cf9cdd3132fa9dd8aaaf9c01442bf12d6614fd")));
+
+ var client = ClientV2.from(connection);
+ var senderInfo = client.getAccountInfo(BlockQuery.BEST, AccountQuery.from(sender));
+ var nonce = senderInfo.getNonce();
+ var txnHash = client.sendTransaction(
+ TransactionFactory
+ .newTokenUpdate( TokenUpdate
+ .builder()
+ .tokenSymbol(tokenSymbol)
+ .operation(
+ MintTokenOperation
+ .builder()
+ .amount(amount)
+ .build()
+ ).build()
+ )
+ .nonce(nonce)
+ .expiry(expiry)
+ .sender(sender)
+ .sign(signer)
+ );
+ System.out.println(txnHash);
+ Optional finalizedBlockItem = client.waitUntilFinalized(txnHash, this.timeout);
+ System.out.println(finalizedBlockItem);
+ return 0;
+ }
+
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new MintToken()).execute(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/concordium-sdk/pom.xml b/concordium-sdk/pom.xml
index e41d807ec..64208ad9c 100644
--- a/concordium-sdk/pom.xml
+++ b/concordium-sdk/pom.xml
@@ -7,7 +7,7 @@
com.concordium.sdk
concordium-sdk-base
- 12.1.0-alpha.1
+ 12.1.0-alpha.2
concordium-sdk
diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/MintTokenOperation.java b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/MintTokenOperation.java
new file mode 100644
index 000000000..9c47d24eb
--- /dev/null
+++ b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/MintTokenOperation.java
@@ -0,0 +1,39 @@
+package com.concordium.sdk.transactions.tokens;
+
+import com.concordium.sdk.types.UInt64;
+import lombok.*;
+import lombok.extern.jackson.Jacksonized;
+
+import java.util.Optional;
+
+/**
+ * Mint Token
+ * @see CBOR Schema
+ */
+@Getter
+@Builder
+@EqualsAndHashCode
+@ToString
+// This is for CBOR serialization to use the generated builder.
+@Jacksonized
+public class MintTokenOperation implements TokenOperation {
+
+ /**
+ * Amount to be transferred.
+ * It very important that the decimals in it match the actual value of the token.
+ */
+ @NonNull
+ private final TokenOperationAmount amount;
+
+ @Override
+ public UInt64 getBaseCost() {
+ return UInt64.from(50);
+ }
+
+ @Override
+ public String getType() {
+ return TYPE;
+ }
+
+ public static final String TYPE = "mint";
+}
diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TokenOperation.java b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TokenOperation.java
index 3dd20dc12..6061cc3a7 100644
--- a/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TokenOperation.java
+++ b/concordium-sdk/src/main/java/com/concordium/sdk/transactions/tokens/TokenOperation.java
@@ -33,6 +33,10 @@
@JsonSubTypes.Type(
value = TransferTokenOperation.class,
name = TransferTokenOperation.TYPE
+ ),
+ @JsonSubTypes.Type(
+ value = MintTokenOperation.class,
+ name = MintTokenOperation.TYPE
)
})
public interface TokenOperation {
diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/tokens/MintTokenOperationTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/tokens/MintTokenOperationTest.java
new file mode 100644
index 000000000..310a26d8e
--- /dev/null
+++ b/concordium-sdk/src/test/java/com/concordium/sdk/tokens/MintTokenOperationTest.java
@@ -0,0 +1,37 @@
+package com.concordium.sdk.tokens;
+
+import com.concordium.sdk.serializing.CborMapper;
+import com.concordium.sdk.transactions.tokens.*;
+import com.concordium.sdk.types.AccountAddress;
+import com.concordium.sdk.types.UInt64;
+import lombok.SneakyThrows;
+import lombok.val;
+import org.bouncycastle.util.encoders.Hex;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.math.BigDecimal;
+
+public class MintTokenOperationTest {
+
+ @Test
+ @SneakyThrows
+ public void testMintTokenOperationSerialization() {
+ val operation = MintTokenOperation
+ .builder()
+ .amount(new TokenOperationAmount(UInt64.from("15"), 6))
+ .build();
+ val expectedHex = "bf646d696e74bf66616d6f756e74c482250fffff";
+ Assert.assertEquals(
+ expectedHex,
+ Hex.toHexString(CborMapper.INSTANCE.writeValueAsBytes(operation))
+ );
+ Assert.assertEquals(
+ operation,
+ CborMapper.INSTANCE.readValue(
+ Hex.decode(expectedHex),
+ TokenOperation.class
+ )
+ );
+ }
+}
diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/transactions/TokenUpdateTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/transactions/TokenUpdateTest.java
index b7f7106b0..360f03d6a 100644
--- a/concordium-sdk/src/test/java/com/concordium/sdk/transactions/TokenUpdateTest.java
+++ b/concordium-sdk/src/test/java/com/concordium/sdk/transactions/TokenUpdateTest.java
@@ -1,9 +1,6 @@
package com.concordium.sdk.transactions;
-import com.concordium.sdk.transactions.tokens.CborMemo;
-import com.concordium.sdk.transactions.tokens.TaggedTokenHolderAccount;
-import com.concordium.sdk.transactions.tokens.TokenOperationAmount;
-import com.concordium.sdk.transactions.tokens.TransferTokenOperation;
+import com.concordium.sdk.transactions.tokens.*;
import com.concordium.sdk.types.AccountAddress;
import com.concordium.sdk.types.Nonce;
import com.concordium.sdk.types.UInt64;
@@ -230,4 +227,38 @@ public void testTokenUpdatePayloadSerialization() {
TokenUpdate.fromBytes(ByteBuffer.wrap(Hex.decode(expectedHex)))
);
}
+
+
+ @Test
+ @SneakyThrows
+ public void testTokenUpdateMintTransactionEnergy() {
+ Assert.assertEquals(
+ UInt64.from("642"),
+ TransactionFactory
+ .newTokenUpdate(
+ TokenUpdate
+ .builder()
+ .tokenSymbol("TEST")
+ .operation(
+ MintTokenOperation
+ .builder()
+ .amount(
+ new TokenOperationAmount(
+ new BigDecimal("1.5"),
+ 2
+ )
+ )
+ .build()
+ )
+ .build()
+ )
+ .sender(AccountAddress.from("3JwD2Wm3nMbsowCwb1iGEpnt47UQgdrtnq2qT6opJc3z2AgCrc"))
+ .nonce(Nonce.from(78910))
+ .expiry(Expiry.from(123456))
+ .sign(TransactionTestHelper.getValidSigner())
+ .getHeader()
+ .getMaxEnergyCost()
+ );
+ }
+
}
diff --git a/pom.xml b/pom.xml
index 6909f722b..4ba46ec0b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.concordium.sdk
concordium-sdk-base
- 12.1.0-alpha.1
+ 12.1.0-alpha.2
pom
https://github.com/Concordium/concordium-java-sdk