diff --git a/canton/BUILD.bazel b/canton/BUILD.bazel index 65be91512a..ee7346ee5f 100644 --- a/canton/BUILD.bazel +++ b/canton/BUILD.bazel @@ -319,6 +319,7 @@ scala_library( "//canton:community_util-logging", "//canton:daml-common-staging_daml-errors", "//daml-lf/data", + "//daml-lf/language", "//daml-lf/transaction", "//daml-lf/transaction:transaction_proto_java", "//daml-lf/transaction:value_proto_java", diff --git a/canton/community/app/src/pack/examples/06-messaging/contact/daml.yaml b/canton/community/app/src/pack/examples/06-messaging/contact/daml.yaml index 9c63ee7c16..d36875cf6b 100644 --- a/canton/community/app/src/pack/examples/06-messaging/contact/daml.yaml +++ b/canton/community/app/src/pack/examples/06-messaging/contact/daml.yaml @@ -1,4 +1,4 @@ -sdk-version: 2.8.0-snapshot.20231125.12402.0.v9caae0b1 +sdk-version: 2.9.0-snapshot.20231128.12429.0.vcd189081 sandbox-options: - --wall-clock-time name: contact diff --git a/canton/community/app/src/pack/examples/06-messaging/message/daml.yaml b/canton/community/app/src/pack/examples/06-messaging/message/daml.yaml index adff54da25..65fd0b8192 100644 --- a/canton/community/app/src/pack/examples/06-messaging/message/daml.yaml +++ b/canton/community/app/src/pack/examples/06-messaging/message/daml.yaml @@ -1,4 +1,4 @@ -sdk-version: 2.8.0-snapshot.20231125.12402.0.v9caae0b1 +sdk-version: 2.9.0-snapshot.20231128.12429.0.vcd189081 sandbox-options: - --wall-clock-time name: message diff --git a/canton/community/base/src/main/scala/com/digitalasset/canton/protocol/GlobalKeySerialization.scala b/canton/community/base/src/main/scala/com/digitalasset/canton/protocol/GlobalKeySerialization.scala index 1f675e48ac..570d6e7196 100644 --- a/canton/community/base/src/main/scala/com/digitalasset/canton/protocol/GlobalKeySerialization.scala +++ b/canton/community/base/src/main/scala/com/digitalasset/canton/protocol/GlobalKeySerialization.scala @@ -4,7 +4,8 @@ package com.digitalasset.canton.protocol import cats.syntax.either.* -import com.daml.lf.value.ValueCoder.{CidEncoder as LfDummyCidEncoder} +import com.daml.lf.transaction.Util +import com.daml.lf.value.ValueCoder.CidEncoder as LfDummyCidEncoder import com.daml.lf.value.{ValueCoder, ValueOuterClass} import com.digitalasset.canton.serialization.ProtoConverter import com.digitalasset.canton.serialization.ProtoConverter.ParsingResult @@ -54,7 +55,7 @@ object GlobalKeySerialization { ) globalKey <- LfGlobalKey - .build(templateId, versionedKey.unversioned) + .build(templateId, versionedKey.unversioned, Util.sharedKey(versionedKey.version)) .leftMap(err => ProtoDeserializationError.ValueDeserializationError("GlobalKey.key", err.toString) ) diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ActiveContracts.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ActiveContracts.java new file mode 100644 index 0000000000..f091460278 --- /dev/null +++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ActiveContracts.java @@ -0,0 +1,55 @@ +// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates. +// Proprietary code. All rights reserved. + +package com.daml.ledger.javaapi.data; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import org.checkerframework.checker.nullness.qual.NonNull; + +public final class ActiveContracts { + + public final Optional offset; + + public final List activeContracts; + + public final String workflowId; + + public ActiveContracts( + @NonNull Optional offset, + @NonNull List activeContracts, + @NonNull String workflowId) { + this.offset = offset; + this.activeContracts = activeContracts; + this.workflowId = workflowId; + } + + @Override + public String toString() { + return "ActiveContracts{" + + "offset='" + + offset + + '\'' + + ", activeContracts=" + + activeContracts + + ", workflowId=" + + workflowId + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ActiveContracts that = (ActiveContracts) o; + return offset.equals(that.offset) + && Objects.equals(activeContracts, that.activeContracts) + && Objects.equals(workflowId, that.workflowId); + } + + @Override + public int hashCode() { + return Objects.hash(offset, activeContracts, workflowId); + } +} diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ArchivedEvent.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ArchivedEvent.java new file mode 100644 index 0000000000..430dbb5319 --- /dev/null +++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ArchivedEvent.java @@ -0,0 +1,105 @@ +// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates. +// Proprietary code. All rights reserved. + +package com.daml.ledger.javaapi.data; + +import com.daml.ledger.api.v1.EventOuterClass; +import java.util.List; +import java.util.Objects; +import org.checkerframework.checker.nullness.qual.NonNull; + +public final class ArchivedEvent implements Event { + + private final List witnessParties; + + private final String eventId; + + private final Identifier templateId; + + private final String contractId; + + public ArchivedEvent( + @NonNull List<@NonNull String> witnessParties, + @NonNull String eventId, + @NonNull Identifier templateId, + @NonNull String contractId) { + this.witnessParties = witnessParties; + this.eventId = eventId; + this.templateId = templateId; + this.contractId = contractId; + } + + @NonNull + @Override + public List<@NonNull String> getWitnessParties() { + return witnessParties; + } + + @NonNull + @Override + public String getEventId() { + return eventId; + } + + @NonNull + @Override + public Identifier getTemplateId() { + return templateId; + } + + @NonNull + @Override + public String getContractId() { + return contractId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ArchivedEvent that = (ArchivedEvent) o; + return Objects.equals(witnessParties, that.witnessParties) + && Objects.equals(eventId, that.eventId) + && Objects.equals(templateId, that.templateId) + && Objects.equals(contractId, that.contractId); + } + + @Override + public int hashCode() { + + return Objects.hash(witnessParties, eventId, templateId, contractId); + } + + @Override + public String toString() { + return "ArchivedEvent{" + + "witnessParties=" + + witnessParties + + ", eventId='" + + eventId + + '\'' + + ", templateId=" + + templateId + + ", contractId='" + + contractId + + '\'' + + '}'; + } + + public EventOuterClass.ArchivedEvent toProto() { + return EventOuterClass.ArchivedEvent.newBuilder() + .setContractId(getContractId()) + .setEventId(getEventId()) + .setTemplateId(getTemplateId().toProto()) + .addAllWitnessParties(this.getWitnessParties()) + .build(); + } + + public static ArchivedEvent fromProto(EventOuterClass.ArchivedEvent archivedEvent) { + return new ArchivedEvent( + archivedEvent.getWitnessPartiesList(), + archivedEvent.getEventId(), + Identifier.fromProto(archivedEvent.getTemplateId()), + archivedEvent.getContractId()); + } +} diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Bool.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Bool.java new file mode 100644 index 0000000000..66f853f433 --- /dev/null +++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Bool.java @@ -0,0 +1,58 @@ +// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates. +// Proprietary code. All rights reserved. + +package com.daml.ledger.javaapi.data; + +import com.daml.ledger.api.v1.ValueOuterClass; +import java.util.Objects; + +public final class Bool extends Value { + + private final boolean value; + + public static final Bool TRUE = new Bool(true); + public static final Bool FALSE = new Bool(false); + + // TODO i15639 make private; delete equals/hashCode + /** @deprecated Use {@link #of} instead; since Daml 2.5.0 */ + @Deprecated + public Bool(boolean value) { + this.value = value; + } + + public static Bool of(boolean value) { + return value ? TRUE : FALSE; + } + + @Override + public ValueOuterClass.Value toProto() { + return ValueOuterClass.Value.newBuilder().setBool(this.value).build(); + } + + public boolean isValue() { + return value; + } + + public boolean getValue() { + return isValue(); + } + + @Override + public String toString() { + return "Bool{" + "value=" + value + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Bool bool = (Bool) o; + return value == bool.value; + } + + @Override + public int hashCode() { + + return Objects.hash(value); + } +} diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Checkpoint.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Checkpoint.java new file mode 100644 index 0000000000..aa60e95cf4 --- /dev/null +++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Checkpoint.java @@ -0,0 +1,68 @@ +// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates. +// Proprietary code. All rights reserved. + +package com.daml.ledger.javaapi.data; + +import com.daml.ledger.api.v1.CommandCompletionServiceOuterClass; +import java.time.Instant; +import java.util.Objects; +import org.checkerframework.checker.nullness.qual.NonNull; + +public final class Checkpoint { + + private final Instant recordTime; + + private final LedgerOffset offset; + + public Checkpoint(@NonNull Instant recordTime, @NonNull LedgerOffset offset) { + this.recordTime = recordTime; + this.offset = offset; + } + + public static Checkpoint fromProto(CommandCompletionServiceOuterClass.Checkpoint checkpoint) { + LedgerOffset offset = LedgerOffset.fromProto(checkpoint.getOffset()); + return new Checkpoint( + Instant.ofEpochSecond( + checkpoint.getRecordTime().getSeconds(), checkpoint.getRecordTime().getNanos()), + offset); + } + + public CommandCompletionServiceOuterClass.Checkpoint toProto() { + return CommandCompletionServiceOuterClass.Checkpoint.newBuilder() + .setRecordTime( + com.google.protobuf.Timestamp.newBuilder() + .setSeconds(this.recordTime.getEpochSecond()) + .setNanos(this.recordTime.getNano()) + .build()) + .setOffset(this.offset.toProto()) + .build(); + } + + public @NonNull Instant getRecordTime() { + return recordTime; + } + + @NonNull + public LedgerOffset getOffset() { + return offset; + } + + @Override + public String toString() { + return "Checkpoint{" + "recordTime=" + recordTime + ", offset=" + offset + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Checkpoint that = (Checkpoint) o; + return Objects.equals(recordTime, that.recordTime) && Objects.equals(offset, that.offset); + } + + @Override + public int hashCode() { + + return Objects.hash(recordTime, offset); + } +} diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Command.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Command.java new file mode 100644 index 0000000000..1d8c6306ee --- /dev/null +++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Command.java @@ -0,0 +1,73 @@ +// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates. +// Proprietary code. All rights reserved. + +package com.daml.ledger.javaapi.data; + +import com.daml.ledger.api.v1.CommandsOuterClass; +import com.daml.ledger.javaapi.data.codegen.HasCommands; +import java.util.List; +import java.util.Optional; + +public abstract class Command implements HasCommands { + + abstract Identifier getTemplateId(); + + @Override + public final List commands() { + return List.of(this); + } + + public static Command fromProtoCommand(CommandsOuterClass.Command command) { + switch (command.getCommandCase()) { + case CREATE: + return CreateCommand.fromProto(command.getCreate()); + case EXERCISE: + return ExerciseCommand.fromProto(command.getExercise()); + case CREATEANDEXERCISE: + return CreateAndExerciseCommand.fromProto(command.getCreateAndExercise()); + case EXERCISEBYKEY: + return ExerciseByKeyCommand.fromProto(command.getExerciseByKey()); + case COMMAND_NOT_SET: + default: + throw new ProtoCommandUnknown(command); + } + } + + public CommandsOuterClass.Command toProtoCommand() { + CommandsOuterClass.Command.Builder builder = CommandsOuterClass.Command.newBuilder(); + if (this instanceof CreateCommand) { + builder.setCreate(((CreateCommand) this).toProto()); + } else if (this instanceof ExerciseCommand) { + builder.setExercise(((ExerciseCommand) this).toProto()); + } else if (this instanceof CreateAndExerciseCommand) { + builder.setCreateAndExercise(((CreateAndExerciseCommand) this).toProto()); + } else if (this instanceof ExerciseByKeyCommand) { + builder.setExerciseByKey(((ExerciseByKeyCommand) this).toProto()); + } else { + throw new CommandUnknown(this); + } + return builder.build(); + } + + public final Optional asCreateCommand() { + return (this instanceof CreateCommand) ? Optional.of((CreateCommand) this) : Optional.empty(); + } + + public final Optional asExerciseCommand() { + return (this instanceof ExerciseCommand) + ? Optional.of((ExerciseCommand) this) + : Optional.empty(); + } +} + +class CommandUnknown extends RuntimeException { + public CommandUnknown(Command command) { + super("Command unknown " + command.toString()); + } +} + +class ProtoCommandUnknown extends RuntimeException { + public ProtoCommandUnknown(CommandsOuterClass.Command command) { + super("Command unknown " + command.toString()); + } +} diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CommandsSubmission.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CommandsSubmission.java new file mode 100644 index 0000000000..47092e3481 --- /dev/null +++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CommandsSubmission.java @@ -0,0 +1,278 @@ +// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates. +// Proprietary code. All rights reserved. + +package com.daml.ledger.javaapi.data; + +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; +import static java.util.Optional.empty; + +import com.daml.ledger.javaapi.data.codegen.HasCommands; +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * This class can be used to build a valid submission. It provides {@link #create(String, String, List)} + * for initial creation and methods to set optional parameters + * e.g {@link #withActAs(List)}, {@link #withWorkflowId(String)} etc. + * + * Usage: + *
+ *   var submission = CommandsSubmission.create(applicationId, commandId, commands)
+ *                                   .withAccessToken(token)
+ *                                   .withParty(party)
+ *                                   .with...
+ * 
+ */
+public final class CommandsSubmission {
+  private String applicationId;
+  private String commandId;
+  private List<@NonNull ? extends HasCommands> commands;
+
+  private Optional workflowId;
+  private List<@NonNull String> actAs;
+  private List<@NonNull String> readAs;
+  private Optional minLedgerTimeAbs;
+  private Optional minLedgerTimeRel;
+  private Optional deduplicationTime;
+  private Optional accessToken;
+  private List disclosedContracts;
+
+  protected CommandsSubmission(
+      String applicationId,
+      String commandId,
+      List<@NonNull ? extends HasCommands> commands,
+      List<@NonNull String> actAs,
+      List<@NonNull String> readAs,
+      Optional workflowId,
+      Optional minLedgerTimeAbs,
+      Optional minLedgerTimeRel,
+      Optional deduplicationTime,
+      Optional accessToken,
+      List<@NonNull DisclosedContract> disclosedContracts) {
+    this.workflowId = workflowId;
+    this.applicationId = applicationId;
+    this.commandId = commandId;
+    this.actAs = actAs;
+    this.readAs = readAs;
+    this.minLedgerTimeAbs = minLedgerTimeAbs;
+    this.minLedgerTimeRel = minLedgerTimeRel;
+    this.deduplicationTime = deduplicationTime;
+    this.commands = commands;
+    this.accessToken = accessToken;
+    this.disclosedContracts = disclosedContracts;
+  }
+
+  public static CommandsSubmission create(
+      String applicationId, String commandId, List<@NonNull ? extends HasCommands> commands) {
+    return new CommandsSubmission(
+        applicationId,
+        commandId,
+        commands,
+        emptyList(),
+        emptyList(),
+        empty(),
+        empty(),
+        Optional.empty(),
+        empty(),
+        empty(),
+        emptyList());
+  }
+
+  public Optional getWorkflowId() {
+    return workflowId;
+  }
+
+  public String getApplicationId() {
+    return applicationId;
+  }
+
+  public String getCommandId() {
+    return commandId;
+  }
+
+  public List getActAs() {
+    return unmodifiableList(actAs);
+  }
+
+  public List getReadAs() {
+    return unmodifiableList(readAs);
+  }
+
+  public Optional getMinLedgerTimeAbs() {
+    return minLedgerTimeAbs;
+  }
+
+  public Optional getMinLedgerTimeRel() {
+    return minLedgerTimeRel;
+  }
+
+  public Optional getDeduplicationTime() {
+    return deduplicationTime;
+  }
+
+  public List getCommands() {
+    return unmodifiableList(commands);
+  }
+
+  public Optional getAccessToken() {
+    return accessToken;
+  }
+
+  public List getDisclosedContracts() {
+    return unmodifiableList(disclosedContracts);
+  }
+
+  public CommandsSubmission withWorkflowId(String workflowId) {
+    return new CommandsSubmission(
+        applicationId,
+        commandId,
+        commands,
+        actAs,
+        readAs,
+        Optional.of(workflowId),
+        minLedgerTimeAbs,
+        minLedgerTimeRel,
+        deduplicationTime,
+        accessToken,
+        disclosedContracts);
+  }
+
+  public CommandsSubmission withActAs(String actAs) {
+    return new CommandsSubmission(
+        applicationId,
+        commandId,
+        commands,
+        List.of(actAs),
+        readAs,
+        workflowId,
+        minLedgerTimeAbs,
+        minLedgerTimeRel,
+        deduplicationTime,
+        accessToken,
+        disclosedContracts);
+  }
+
+  public CommandsSubmission withActAs(List<@NonNull String> actAs) {
+    return new CommandsSubmission(
+        applicationId,
+        commandId,
+        commands,
+        actAs,
+        readAs,
+        workflowId,
+        minLedgerTimeAbs,
+        minLedgerTimeRel,
+        deduplicationTime,
+        accessToken,
+        disclosedContracts);
+  }
+
+  public CommandsSubmission withReadAs(List<@NonNull String> readAs) {
+    return new CommandsSubmission(
+        applicationId,
+        commandId,
+        commands,
+        actAs,
+        readAs,
+        workflowId,
+        minLedgerTimeAbs,
+        minLedgerTimeRel,
+        deduplicationTime,
+        accessToken,
+        disclosedContracts);
+  }
+
+  public CommandsSubmission withMinLedgerTimeAbs(Optional minLedgerTimeAbs) {
+    return new CommandsSubmission(
+        applicationId,
+        commandId,
+        commands,
+        actAs,
+        readAs,
+        workflowId,
+        minLedgerTimeAbs,
+        minLedgerTimeRel,
+        deduplicationTime,
+        accessToken,
+        disclosedContracts);
+  }
+
+  public CommandsSubmission withMinLedgerTimeRel(Optional minLedgerTimeRel) {
+    return new CommandsSubmission(
+        applicationId,
+        commandId,
+        commands,
+        actAs,
+        readAs,
+        workflowId,
+        minLedgerTimeAbs,
+        minLedgerTimeRel,
+        deduplicationTime,
+        accessToken,
+        disclosedContracts);
+  }
+
+  public CommandsSubmission withDeduplicationTime(Optional deduplicationTime) {
+    return new CommandsSubmission(
+        applicationId,
+        commandId,
+        commands,
+        actAs,
+        readAs,
+        workflowId,
+        minLedgerTimeAbs,
+        minLedgerTimeRel,
+        deduplicationTime,
+        accessToken,
+        disclosedContracts);
+  }
+
+  public CommandsSubmission withCommands(List<@NonNull ? extends HasCommands> commands) {
+    return new CommandsSubmission(
+        applicationId,
+        commandId,
+        commands,
+        actAs,
+        readAs,
+        workflowId,
+        minLedgerTimeAbs,
+        minLedgerTimeRel,
+        deduplicationTime,
+        accessToken,
+        disclosedContracts);
+  }
+
+  public CommandsSubmission withAccessToken(Optional accessToken) {
+    return new CommandsSubmission(
+        applicationId,
+        commandId,
+        commands,
+        actAs,
+        readAs,
+        workflowId,
+        minLedgerTimeAbs,
+        minLedgerTimeRel,
+        deduplicationTime,
+        accessToken,
+        disclosedContracts);
+  }
+
+  public CommandsSubmission withDisclosedContracts(List disclosedContracts) {
+    return new CommandsSubmission(
+        applicationId,
+        commandId,
+        commands,
+        actAs,
+        readAs,
+        workflowId,
+        minLedgerTimeAbs,
+        minLedgerTimeRel,
+        deduplicationTime,
+        accessToken,
+        disclosedContracts);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CompletionEndResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CompletionEndResponse.java
new file mode 100644
index 0000000000..d5b6909276
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CompletionEndResponse.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.CommandCompletionServiceOuterClass;
+import java.util.Objects;
+
+public final class CompletionEndResponse {
+
+  private final LedgerOffset offset;
+
+  public CompletionEndResponse(LedgerOffset offset) {
+    this.offset = offset;
+  }
+
+  public static CompletionEndResponse fromProto(
+      CommandCompletionServiceOuterClass.CompletionEndResponse response) {
+    return new CompletionEndResponse(LedgerOffset.fromProto(response.getOffset()));
+  }
+
+  public LedgerOffset getOffset() {
+    return offset;
+  }
+
+  @Override
+  public String toString() {
+    return "CompletionEndResponse{" + "offset=" + offset + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CompletionEndResponse that = (CompletionEndResponse) o;
+    return Objects.equals(offset, that.offset);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(offset);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CompletionStreamRequest.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CompletionStreamRequest.java
new file mode 100644
index 0000000000..93e6d8a311
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CompletionStreamRequest.java
@@ -0,0 +1,114 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.CommandCompletionServiceOuterClass;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+public final class CompletionStreamRequest {
+
+  private final String ledgerId;
+
+  private final String applicationId;
+
+  private final Set parties;
+
+  private final Optional offset;
+
+  public static CompletionStreamRequest fromProto(
+      CommandCompletionServiceOuterClass.CompletionStreamRequest request) {
+    String ledgerId = request.getLedgerId();
+    String applicationId = request.getApplicationId();
+    HashSet parties = new HashSet<>(request.getPartiesList());
+    LedgerOffset offset = LedgerOffset.fromProto(request.getOffset());
+    return new CompletionStreamRequest(ledgerId, applicationId, parties, offset);
+  }
+
+  public CommandCompletionServiceOuterClass.CompletionStreamRequest toProto() {
+    CommandCompletionServiceOuterClass.CompletionStreamRequest.Builder protoBuilder =
+        CommandCompletionServiceOuterClass.CompletionStreamRequest.newBuilder()
+            .setLedgerId(this.ledgerId)
+            .setApplicationId(this.applicationId)
+            .addAllParties(this.parties);
+    this.offset.ifPresent(offset -> protoBuilder.setOffset(offset.toProto()));
+    return protoBuilder.build();
+  }
+
+  @Override
+  public String toString() {
+    return "CompletionStreamRequest{"
+        + "ledgerId='"
+        + ledgerId
+        + '\''
+        + ", applicationId='"
+        + applicationId
+        + '\''
+        + ", parties="
+        + parties
+        + ", offset="
+        + offset
+        + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CompletionStreamRequest that = (CompletionStreamRequest) o;
+    return Objects.equals(ledgerId, that.ledgerId)
+        && Objects.equals(applicationId, that.applicationId)
+        && Objects.equals(parties, that.parties)
+        && Objects.equals(offset, that.offset);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(ledgerId, applicationId, parties, offset);
+  }
+
+  public String getLedgerId() {
+
+    return ledgerId;
+  }
+
+  public String getApplicationId() {
+    return applicationId;
+  }
+
+  public Set getParties() {
+    return parties;
+  }
+
+  /**
+   * @deprecated Legacy, nullable version of {@link #getLedgerOffset()}, which should be used
+   *     instead.
+   */
+  @Deprecated
+  public LedgerOffset getOffset() {
+    return offset.orElse(null);
+  }
+
+  public Optional getLedgerOffset() {
+    return offset;
+  }
+
+  public CompletionStreamRequest(String ledgerId, String applicationId, Set parties) {
+    this.ledgerId = ledgerId;
+    this.applicationId = applicationId;
+    this.parties = parties;
+    this.offset = Optional.empty();
+  }
+
+  public CompletionStreamRequest(
+      String ledgerId, String applicationId, Set parties, LedgerOffset offset) {
+    this.ledgerId = ledgerId;
+    this.applicationId = applicationId;
+    this.parties = parties;
+    this.offset = Optional.of(offset);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CompletionStreamResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CompletionStreamResponse.java
new file mode 100644
index 0000000000..c02d2a133c
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CompletionStreamResponse.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.CommandCompletionServiceOuterClass;
+import com.daml.ledger.api.v1.CompletionOuterClass;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class CompletionStreamResponse {
+
+  private final Optional checkpoint;
+
+  private final List completions;
+
+  public CompletionStreamResponse(
+      @NonNull Optional checkpoint,
+      @NonNull List completions) {
+    this.checkpoint = checkpoint;
+    this.completions = completions;
+  }
+
+  public static CompletionStreamResponse fromProto(
+      CommandCompletionServiceOuterClass.CompletionStreamResponse response) {
+    if (response.hasCheckpoint()) {
+      Checkpoint checkpoint = Checkpoint.fromProto(response.getCheckpoint());
+      return new CompletionStreamResponse(Optional.of(checkpoint), response.getCompletionsList());
+    } else {
+      return new CompletionStreamResponse(Optional.empty(), response.getCompletionsList());
+    }
+  }
+
+  public CommandCompletionServiceOuterClass.CompletionStreamResponse toProto() {
+    CommandCompletionServiceOuterClass.CompletionStreamResponse.Builder builder =
+        CommandCompletionServiceOuterClass.CompletionStreamResponse.newBuilder();
+    this.checkpoint.ifPresent(c -> builder.setCheckpoint(c.toProto()));
+    builder.addAllCompletions(this.completions);
+    return builder.build();
+  }
+
+  @NonNull
+  public Optional getCheckpoint() {
+    return checkpoint;
+  }
+
+  @NonNull
+  public List getCompletions() {
+    return completions;
+  }
+
+  @Override
+  public String toString() {
+    return "CompletionStreamResponse{"
+        + "checkpoint="
+        + checkpoint
+        + ", completions="
+        + completions
+        + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CompletionStreamResponse that = (CompletionStreamResponse) o;
+    return Objects.equals(checkpoint, that.checkpoint)
+        && Objects.equals(completions, that.completions);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(checkpoint, completions);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Contract.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Contract.java
new file mode 100644
index 0000000000..fad8ff3c42
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Contract.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+public interface Contract {}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ContractFilter.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ContractFilter.java
new file mode 100644
index 0000000000..a0d484a478
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ContractFilter.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.javaapi.data.codegen.Contract;
+import com.daml.ledger.javaapi.data.codegen.ContractCompanion;
+import com.daml.ledger.javaapi.data.codegen.ContractTypeCompanion;
+import com.daml.ledger.javaapi.data.codegen.InterfaceCompanion;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * This class contains utilities to decode a CreatedEvent and create a 
+ * TransactionFilter by provider parties It can only be instantiated with a subtype of 
+ * ContractCompanion
+ */
+public final class ContractFilter {
+  private final ContractTypeCompanion companion;
+
+  private final Filter filter;
+
+  private ContractFilter(ContractTypeCompanion companion, Filter filter) {
+    this.companion = companion;
+    this.filter = filter;
+  }
+
+  public static  ContractFilter of(ContractCompanion companion) {
+    Filter filter =
+        new InclusiveFilter(
+            Collections.emptyMap(),
+            Collections.singletonMap(
+                companion.TEMPLATE_ID, Filter.Template.HIDE_CREATED_EVENT_BLOB));
+    return new ContractFilter<>(companion, filter);
+  }
+
+  public static  ContractFilter> of(
+      InterfaceCompanion companion) {
+    Filter filter =
+        new InclusiveFilter(
+            Collections.singletonMap(
+                companion.TEMPLATE_ID, Filter.Interface.INCLUDE_VIEW_HIDE_CREATED_EVENT_BLOB),
+            Collections.emptyMap());
+    return new ContractFilter<>(companion, filter);
+  }
+
+  public Ct toContract(CreatedEvent createdEvent) throws IllegalArgumentException {
+    return companion.fromCreatedEvent(createdEvent);
+  }
+
+  public TransactionFilter transactionFilter(Set parties) {
+    return transactionFilter(filter, parties);
+  }
+
+  private static TransactionFilter transactionFilter(Filter filter, Set parties) {
+    Map partyToFilters =
+        parties.stream().collect(Collectors.toMap(Function.identity(), x -> filter));
+    return new FiltersByParty(partyToFilters);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ContractId.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ContractId.java
new file mode 100644
index 0000000000..869df576fa
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ContractId.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.util.Objects;
+
+public final class ContractId extends Value {
+
+  private final String value;
+
+  public ContractId(String value) {
+    this.value = value;
+  }
+
+  public String getValue() {
+    return value;
+  }
+
+  @Override
+  public ValueOuterClass.Value toProto() {
+    return ValueOuterClass.Value.newBuilder().setContractId(this.value).build();
+  }
+
+  @Override
+  public String toString() {
+    return "ContractId{" + "value='" + value + '\'' + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ContractId that = (ContractId) o;
+    return Objects.equals(value, that.value);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(value);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ContractMetadata.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ContractMetadata.java
new file mode 100644
index 0000000000..934c2af56b
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ContractMetadata.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ContractMetadataOuterClass;
+import com.google.protobuf.ByteString;
+import java.time.Instant;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class ContractMetadata {
+
+  // Note that we can't use a `com.daml.ledger.javaapi.data.Timestamp` here because
+  // it only supports milliseconds-precision and we require lossless conversions through
+  // from/toProto.
+  public final Instant createdAt;
+  public final ByteString driverMetadata;
+  public final ByteString contractKeyHash;
+
+  public static ContractMetadata Empty() {
+    return new ContractMetadata(Instant.EPOCH, ByteString.EMPTY, ByteString.EMPTY);
+  }
+
+  public ContractMetadata(
+      @NonNull Instant createdAt,
+      @NonNull ByteString contractKeyHash,
+      @NonNull ByteString driverMetadata) {
+    this.createdAt = createdAt;
+    this.contractKeyHash = contractKeyHash;
+    this.driverMetadata = driverMetadata;
+  }
+
+  @NonNull
+  public static ContractMetadata fromProto(ContractMetadataOuterClass.ContractMetadata metadata) {
+    return new ContractMetadata(
+        Instant.ofEpochSecond(
+            metadata.getCreatedAt().getSeconds(), metadata.getCreatedAt().getNanos()),
+        metadata.getContractKeyHash(),
+        metadata.getDriverMetadata());
+  }
+
+  public ContractMetadataOuterClass.ContractMetadata toProto() {
+    return ContractMetadataOuterClass.ContractMetadata.newBuilder()
+        .setCreatedAt(
+            com.google.protobuf.Timestamp.newBuilder()
+                .setSeconds(this.createdAt.getEpochSecond())
+                .setNanos(this.createdAt.getNano())
+                .build())
+        .setContractKeyHash(this.contractKeyHash)
+        .setDriverMetadata(this.driverMetadata)
+        .build();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ContractMetadata that = (ContractMetadata) o;
+    return Objects.equals(createdAt, that.createdAt)
+        && Objects.equals(contractKeyHash, that.contractKeyHash)
+        && Objects.equals(driverMetadata, that.driverMetadata);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(createdAt, contractKeyHash, driverMetadata);
+  }
+
+  @Override
+  public String toString() {
+    return "ContractMetadata{"
+        + "createdAt='"
+        + createdAt
+        + '\''
+        + ", contractKeyHash='"
+        + contractKeyHash
+        + '\''
+        + ", driverMetadata='"
+        + driverMetadata
+        + '\''
+        + '}';
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreateAndExerciseCommand.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreateAndExerciseCommand.java
new file mode 100644
index 0000000000..39f8cce3ea
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreateAndExerciseCommand.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.CommandsOuterClass;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class CreateAndExerciseCommand extends Command {
+  private final Identifier templateId;
+
+  private final DamlRecord createArguments;
+
+  private final String choice;
+
+  private final Value choiceArgument;
+
+  public CreateAndExerciseCommand(
+      @NonNull Identifier templateId,
+      @NonNull DamlRecord createArguments,
+      @NonNull String choice,
+      @NonNull Value choiceArgument) {
+    this.templateId = templateId;
+    this.createArguments = createArguments;
+    this.choice = choice;
+    this.choiceArgument = choiceArgument;
+  }
+
+  public static CreateAndExerciseCommand fromProto(
+      CommandsOuterClass.CreateAndExerciseCommand command) {
+    Identifier templateId = Identifier.fromProto(command.getTemplateId());
+    DamlRecord createArguments = DamlRecord.fromProto(command.getCreateArguments());
+    String choice = command.getChoice();
+    Value choiceArgument = Value.fromProto(command.getChoiceArgument());
+    return new CreateAndExerciseCommand(templateId, createArguments, choice, choiceArgument);
+  }
+
+  public CommandsOuterClass.CreateAndExerciseCommand toProto() {
+    return CommandsOuterClass.CreateAndExerciseCommand.newBuilder()
+        .setTemplateId(this.templateId.toProto())
+        .setCreateArguments(this.createArguments.toProtoRecord())
+        .setChoice(this.choice)
+        .setChoiceArgument(this.choiceArgument.toProto())
+        .build();
+  }
+
+  @Override
+  Identifier getTemplateId() {
+    return templateId;
+  }
+
+  public DamlRecord getCreateArguments() {
+    return createArguments;
+  }
+
+  public String getChoice() {
+    return choice;
+  }
+
+  public Value getChoiceArgument() {
+    return choiceArgument;
+  }
+
+  @Override
+  public String toString() {
+    return "CreateAndExerciseCommand{"
+        + "templateId="
+        + templateId
+        + ", createArguments="
+        + createArguments
+        + ", choice='"
+        + choice
+        + '\''
+        + ", choiceArgument="
+        + choiceArgument
+        + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CreateAndExerciseCommand that = (CreateAndExerciseCommand) o;
+    return templateId.equals(that.templateId)
+        && createArguments.equals(that.createArguments)
+        && choice.equals(that.choice)
+        && choiceArgument.equals(that.choiceArgument);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(templateId, createArguments, choice, choiceArgument);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreateCommand.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreateCommand.java
new file mode 100644
index 0000000000..7ecd7210f1
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreateCommand.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.CommandsOuterClass;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class CreateCommand extends Command {
+
+  private final Identifier templateId;
+
+  private final DamlRecord createArguments;
+
+  public CreateCommand(@NonNull Identifier templateId, @NonNull DamlRecord createArguments) {
+    this.templateId = templateId;
+    this.createArguments = createArguments;
+  }
+
+  @Override
+  public String toString() {
+    return "CreateCommand{"
+        + "templateId="
+        + templateId
+        + ", createArguments="
+        + createArguments
+        + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CreateCommand that = (CreateCommand) o;
+    return Objects.equals(templateId, that.templateId)
+        && Objects.equals(createArguments, that.createArguments);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(templateId, createArguments);
+  }
+
+  @NonNull
+  @Override
+  public Identifier getTemplateId() {
+    return templateId;
+  }
+
+  @NonNull
+  public DamlRecord getCreateArguments() {
+    return createArguments;
+  }
+
+  public static CreateCommand fromProto(CommandsOuterClass.CreateCommand create) {
+    DamlRecord createArgument = DamlRecord.fromProto(create.getCreateArguments());
+    Identifier templateId = Identifier.fromProto(create.getTemplateId());
+    return new CreateCommand(templateId, createArgument);
+  }
+
+  public CommandsOuterClass.CreateCommand toProto() {
+    return CommandsOuterClass.CreateCommand.newBuilder()
+        .setTemplateId(this.templateId.toProto())
+        .setCreateArguments(this.createArguments.toProtoRecord())
+        .build();
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreateUserRequest.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreateUserRequest.java
new file mode 100644
index 0000000000..5d5b3b46ca
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreateUserRequest.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class CreateUserRequest {
+
+  private final User user;
+  private final List rights;
+
+  public CreateUserRequest(User user, User.Right right, User.Right... rights) {
+    this.user = user;
+    this.rights = new ArrayList<>(rights.length + 1);
+    this.rights.add(right);
+    this.rights.addAll(Arrays.asList(rights));
+  }
+
+  public CreateUserRequest(@NonNull String id, @NonNull String primaryParty) {
+    this(new User(id, primaryParty), new User.Right.CanActAs(primaryParty));
+  }
+
+  public User getUser() {
+    return user;
+  }
+
+  public List getRights() {
+    return new ArrayList<>(rights);
+  }
+
+  public UserManagementServiceOuterClass.CreateUserRequest toProto() {
+    return UserManagementServiceOuterClass.CreateUserRequest.newBuilder()
+        .setUser(this.user.toProto())
+        .addAllRights(this.rights.stream().map(User.Right::toProto).collect(Collectors.toList()))
+        .build();
+  }
+
+  @Override
+  public String toString() {
+    return "CreateUserRequest{" + "user=" + user + ", rights=" + rights + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CreateUserRequest that = (CreateUserRequest) o;
+    return user.equals(that.user) && rights.equals(that.rights);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(user, rights);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreateUserResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreateUserResponse.java
new file mode 100644
index 0000000000..10eaad106f
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreateUserResponse.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+import java.util.Objects;
+
+public final class CreateUserResponse {
+
+  private final User user;
+
+  public CreateUserResponse(User user) {
+    this.user = user;
+  }
+
+  public User getUser() {
+    return user;
+  }
+
+  public static CreateUserResponse fromProto(
+      UserManagementServiceOuterClass.CreateUserResponse proto) {
+    return new CreateUserResponse(User.fromProto(proto.getUser()));
+  }
+
+  public UserManagementServiceOuterClass.CreateUserResponse toProto() {
+    return UserManagementServiceOuterClass.CreateUserResponse.newBuilder()
+        .setUser(this.user.toProto())
+        .build();
+  }
+
+  @Override
+  public String toString() {
+    return "CreateUserResponse{" + "user=" + user + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CreateUserResponse that = (CreateUserResponse) o;
+    return user.equals(that.user);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(user);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreatedEvent.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreatedEvent.java
new file mode 100644
index 0000000000..091b5892f5
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/CreatedEvent.java
@@ -0,0 +1,361 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.EventOuterClass;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.StringValue;
+import com.google.rpc.Status;
+import java.time.Instant;
+import java.util.*;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class CreatedEvent implements Event, TreeEvent {
+
+  private final @NonNull List<@NonNull String> witnessParties;
+
+  private final String eventId;
+
+  private final Identifier templateId;
+
+  private final String contractId;
+
+  private final DamlRecord arguments;
+
+  private final @NonNull Map<@NonNull Identifier, @NonNull DamlRecord> interfaceViews;
+
+  private final @NonNull Map<@NonNull Identifier, @NonNull Status> failedInterfaceViews;
+
+  private final Optional agreementText;
+
+  private final Optional contractKey;
+
+  private final @NonNull Set<@NonNull String> signatories;
+
+  private final @NonNull Set<@NonNull String> observers;
+
+  private final @NonNull ByteString createdEventBlob;
+
+  // Note that we can't use a `com.daml.ledger.javaapi.data.Timestamp` here because
+  // it only supports microseconds-precision and we require lossless conversions through
+  // from/toProto.
+  public final @NonNull Instant createdAt;
+
+  public CreatedEvent(
+      @NonNull List<@NonNull String> witnessParties,
+      @NonNull String eventId,
+      @NonNull Identifier templateId,
+      @NonNull String contractId,
+      @NonNull DamlRecord arguments,
+      @NonNull ByteString createdEventBlob,
+      @NonNull Map<@NonNull Identifier, @NonNull DamlRecord> interfaceViews,
+      @NonNull Map<@NonNull Identifier, com.google.rpc.@NonNull Status> failedInterfaceViews,
+      @NonNull Optional agreementText,
+      @NonNull Optional contractKey,
+      @NonNull Collection<@NonNull String> signatories,
+      @NonNull Collection<@NonNull String> observers,
+      @NonNull Instant createdAt) {
+    this.witnessParties = List.copyOf(witnessParties);
+    this.eventId = eventId;
+    this.templateId = templateId;
+    this.contractId = contractId;
+    this.arguments = arguments;
+    this.createdEventBlob = createdEventBlob;
+    this.interfaceViews = Map.copyOf(interfaceViews);
+    this.failedInterfaceViews = Map.copyOf(failedInterfaceViews);
+    this.agreementText = agreementText;
+    this.contractKey = contractKey;
+    this.signatories = Set.copyOf(signatories);
+    this.observers = Set.copyOf(observers);
+    this.createdAt = createdAt;
+  }
+
+  /**
+   * @deprecated You should pass {@code createArgumentsBlob} and {@code contractMetadata} arguments
+   *     as well. Since Daml 2.6.0
+   */
+  @Deprecated
+  public CreatedEvent(
+      @NonNull List<@NonNull String> witnessParties,
+      @NonNull String eventId,
+      @NonNull Identifier templateId,
+      @NonNull String contractId,
+      @NonNull DamlRecord arguments,
+      @NonNull Map<@NonNull Identifier, @NonNull DamlRecord> interfaceViews,
+      @NonNull Map<@NonNull Identifier, com.google.rpc.@NonNull Status> failedInterfaceViews,
+      @NonNull Optional agreementText,
+      @NonNull Optional contractKey,
+      @NonNull Collection<@NonNull String> signatories,
+      @NonNull Collection<@NonNull String> observers) {
+    this(
+        witnessParties,
+        eventId,
+        templateId,
+        contractId,
+        arguments,
+        ByteString.EMPTY,
+        interfaceViews,
+        failedInterfaceViews,
+        agreementText,
+        contractKey,
+        signatories,
+        observers,
+        Instant.EPOCH);
+  }
+
+  /**
+   * @deprecated Pass {@code interfaceViews} and {@code failedInterfaceViews} arguments; empty maps
+   *     are reasonable defaults. Since Daml 2.4.0
+   */
+  @Deprecated
+  public CreatedEvent(
+      @NonNull List<@NonNull String> witnessParties,
+      @NonNull String eventId,
+      @NonNull Identifier templateId,
+      @NonNull String contractId,
+      @NonNull DamlRecord arguments,
+      @NonNull Optional agreementText,
+      @NonNull Optional contractKey,
+      @NonNull Collection<@NonNull String> signatories,
+      @NonNull Collection<@NonNull String> observers) {
+    this(
+        witnessParties,
+        eventId,
+        templateId,
+        contractId,
+        arguments,
+        Collections.emptyMap(),
+        Collections.emptyMap(),
+        agreementText,
+        contractKey,
+        signatories,
+        observers);
+  }
+
+  @NonNull
+  @Override
+  public List<@NonNull String> getWitnessParties() {
+    return witnessParties;
+  }
+
+  @NonNull
+  @Override
+  public String getEventId() {
+    return eventId;
+  }
+
+  @NonNull
+  @Override
+  public Identifier getTemplateId() {
+    return templateId;
+  }
+
+  @NonNull
+  @Override
+  public String getContractId() {
+    return contractId;
+  }
+
+  @NonNull
+  public DamlRecord getArguments() {
+    return arguments;
+  }
+
+  public ByteString getCreatedEventBlob() {
+    return createdEventBlob;
+  }
+
+  @NonNull
+  public Map<@NonNull Identifier, @NonNull DamlRecord> getInterfaceViews() {
+    return interfaceViews;
+  }
+
+  @NonNull
+  public Map<@NonNull Identifier, @NonNull Status> getFailedInterfaceViews() {
+    return failedInterfaceViews;
+  }
+
+  @NonNull
+  public Optional getAgreementText() {
+    return agreementText;
+  }
+
+  @NonNull
+  public Optional getContractKey() {
+    return contractKey;
+  }
+
+  @NonNull
+  public Set<@NonNull String> getSignatories() {
+    return signatories;
+  }
+
+  @NonNull
+  public Set<@NonNull String> getObservers() {
+    return observers;
+  }
+
+  /**
+   * {@code createdAt} has been introduced in the Ledger API {@link
+   * com.daml.ledger.api.v1.EventOuterClass.CreatedEvent} starting with Canton version 2.8.0. Events
+   * sourced from the Ledger API prior to this version will return the default {@link Instant#EPOCH}
+   * value.
+   */
+  @NonNull
+  public Instant getCreatedAt() {
+    return createdAt;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CreatedEvent that = (CreatedEvent) o;
+    return Objects.equals(witnessParties, that.witnessParties)
+        && Objects.equals(eventId, that.eventId)
+        && Objects.equals(templateId, that.templateId)
+        && Objects.equals(contractId, that.contractId)
+        && Objects.equals(arguments, that.arguments)
+        && Objects.equals(createdEventBlob, that.createdEventBlob)
+        && Objects.equals(interfaceViews, that.interfaceViews)
+        && Objects.equals(failedInterfaceViews, that.failedInterfaceViews)
+        && Objects.equals(agreementText, that.agreementText)
+        && Objects.equals(contractKey, that.contractKey)
+        && Objects.equals(signatories, that.signatories)
+        && Objects.equals(observers, that.observers)
+        && Objects.equals(createdAt, that.createdAt);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(
+        witnessParties,
+        eventId,
+        templateId,
+        contractId,
+        arguments,
+        createdEventBlob,
+        interfaceViews,
+        failedInterfaceViews,
+        agreementText,
+        contractKey,
+        signatories,
+        observers,
+        createdAt);
+  }
+
+  @Override
+  public String toString() {
+    return "CreatedEvent{"
+        + "witnessParties="
+        + witnessParties
+        + ", eventId='"
+        + eventId
+        + '\''
+        + ", templateId="
+        + templateId
+        + ", contractId='"
+        + contractId
+        + '\''
+        + ", arguments="
+        + arguments
+        + ", createdEventBlob="
+        + createdEventBlob
+        + ", interfaceViews="
+        + interfaceViews
+        + ", failedInterfaceViews="
+        + failedInterfaceViews
+        + ", agreementText='"
+        + agreementText
+        + "', contractKey="
+        + contractKey
+        + ", signatories="
+        + signatories
+        + ", observers="
+        + observers
+        + ", createdAt="
+        + createdAt
+        + '}';
+  }
+
+  @SuppressWarnings("deprecation")
+  public EventOuterClass.@NonNull CreatedEvent toProto() {
+    EventOuterClass.CreatedEvent.Builder builder =
+        EventOuterClass.CreatedEvent.newBuilder()
+            .setContractId(this.getContractId())
+            .setCreateArguments(this.getArguments().toProtoRecord())
+            .setCreatedEventBlob(createdEventBlob)
+            .addAllInterfaceViews(
+                Stream.concat(
+                        toProtoInterfaceViews(
+                            interfaceViews, (b, dr) -> b.setViewValue(dr.toProtoRecord())),
+                        toProtoInterfaceViews(
+                            failedInterfaceViews, (b, status) -> b.setViewStatus(status)))
+                    .collect(Collectors.toUnmodifiableList()))
+            .setEventId(this.getEventId())
+            .setTemplateId(this.getTemplateId().toProto())
+            .addAllWitnessParties(this.getWitnessParties())
+            .addAllSignatories(this.getSignatories())
+            .addAllObservers(this.getObservers())
+            .setCreatedAt(
+                com.google.protobuf.Timestamp.newBuilder()
+                    .setSeconds(this.createdAt.getEpochSecond())
+                    .setNanos(this.createdAt.getNano())
+                    .build());
+    agreementText.ifPresent(a -> builder.setAgreementText(StringValue.of(a)));
+    contractKey.ifPresent(a -> builder.setContractKey(a.toProto()));
+    return builder.build();
+  }
+
+  private static  Stream toProtoInterfaceViews(
+      Map views,
+      BiFunction
+          addV) {
+    return views.entrySet().stream()
+        .map(
+            e ->
+                addV.apply(
+                        EventOuterClass.InterfaceView.newBuilder()
+                            .setInterfaceId(e.getKey().toProto()),
+                        e.getValue())
+                    .build());
+  }
+
+  @SuppressWarnings("deprecation")
+  public static CreatedEvent fromProto(EventOuterClass.CreatedEvent createdEvent) {
+    var splitInterfaceViews =
+        createdEvent.getInterfaceViewsList().stream()
+            .collect(Collectors.partitioningBy(EventOuterClass.InterfaceView::hasViewValue));
+    return new CreatedEvent(
+        createdEvent.getWitnessPartiesList(),
+        createdEvent.getEventId(),
+        Identifier.fromProto(createdEvent.getTemplateId()),
+        createdEvent.getContractId(),
+        DamlRecord.fromProto(createdEvent.getCreateArguments()),
+        createdEvent.getCreatedEventBlob(),
+        splitInterfaceViews.get(true).stream()
+            .collect(
+                Collectors.toUnmodifiableMap(
+                    iv -> Identifier.fromProto(iv.getInterfaceId()),
+                    iv -> DamlRecord.fromProto(iv.getViewValue()))),
+        splitInterfaceViews.get(false).stream()
+            .collect(
+                Collectors.toUnmodifiableMap(
+                    iv -> Identifier.fromProto(iv.getInterfaceId()),
+                    EventOuterClass.InterfaceView::getViewStatus)),
+        createdEvent.hasAgreementText()
+            ? Optional.of(createdEvent.getAgreementText().getValue())
+            : Optional.empty(),
+        createdEvent.hasContractKey()
+            ? Optional.of(Value.fromProto(createdEvent.getContractKey()))
+            : Optional.empty(),
+        createdEvent.getSignatoriesList(),
+        createdEvent.getObserversList(),
+        Instant.ofEpochSecond(
+            createdEvent.getCreatedAt().getSeconds(), createdEvent.getCreatedAt().getNanos()));
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlCollectors.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlCollectors.java
new file mode 100644
index 0000000000..8c642ee4d9
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlCollectors.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collector;
+
+public final class DamlCollectors {
+
+  // no instantiation
+  private DamlCollectors() {}
+
+  public static  Collector, DamlList> toDamlList(Function valueMapper) {
+    return Collector.of(
+        ArrayList::new,
+        (acc, entry) -> acc.add(valueMapper.apply(entry)),
+        (left, right) -> {
+          left.addAll(right);
+          return left;
+        },
+        DamlList::fromPrivateList);
+  }
+
+  public static Collector, DamlList> toDamlList() {
+    return toDamlList(Function.identity());
+  }
+
+  public static  Collector, DamlTextMap> toDamlTextMap(
+      Function keyMapper, Function valueMapper) {
+
+    return Collector.of(
+        HashMap::new,
+        (acc, entry) -> acc.put(keyMapper.apply(entry), valueMapper.apply(entry)),
+        (left, right) -> {
+          left.putAll(right);
+          return left;
+        },
+        DamlTextMap::fromPrivateMap);
+  }
+
+  public static Collector, Map, DamlTextMap>
+      toDamlTextMap() {
+    return toDamlTextMap(Map.Entry::getKey, Map.Entry::getValue);
+  }
+
+  public static  Collector, DamlGenMap> toDamlGenMap(
+      Function keyMapper, Function valueMapper) {
+
+    return Collector.of(
+        LinkedHashMap::new,
+        (acc, entry) -> acc.put(keyMapper.apply(entry), valueMapper.apply(entry)),
+        (left, right) -> {
+          left.putAll(right);
+          return left;
+        },
+        DamlGenMap::fromPrivateMap);
+  }
+
+  public static Collector, Map, DamlGenMap> toMap() {
+    return toDamlGenMap(Map.Entry::getKey, Map.Entry::getValue);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlEnum.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlEnum.java
new file mode 100644
index 0000000000..24af2e0969
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlEnum.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.util.Objects;
+import java.util.Optional;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class DamlEnum extends Value {
+
+  private final Optional enumId;
+
+  private final String constructor;
+
+  public DamlEnum(@NonNull Identifier enumId, @NonNull String constructor) {
+    this.enumId = Optional.of(enumId);
+    this.constructor = constructor;
+  }
+
+  public DamlEnum(@NonNull String constructor) {
+    this.enumId = Optional.empty();
+    this.constructor = constructor;
+  }
+
+  public static DamlEnum fromProto(ValueOuterClass.Enum value) {
+    String constructor = value.getConstructor();
+    if (value.hasEnumId()) {
+      Identifier variantId = Identifier.fromProto(value.getEnumId());
+      return new DamlEnum(variantId, constructor);
+    } else {
+      return new DamlEnum(constructor);
+    }
+  }
+
+  @NonNull
+  public Optional getEnumId() {
+    return enumId;
+  }
+
+  @NonNull
+  public String getConstructor() {
+    return constructor;
+  }
+
+  @Override
+  public ValueOuterClass.Value toProto() {
+    return ValueOuterClass.Value.newBuilder().setEnum(this.toProtoEnum()).build();
+  }
+
+  public ValueOuterClass.Enum toProtoEnum() {
+    ValueOuterClass.Enum.Builder builder = ValueOuterClass.Enum.newBuilder();
+    builder.setConstructor(this.getConstructor());
+    this.getEnumId().ifPresent(identifier -> builder.setEnumId(identifier.toProto()));
+    return builder.build();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    DamlEnum value = (DamlEnum) o;
+    return Objects.equals(enumId, value.enumId) && Objects.equals(constructor, value.constructor);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(enumId, constructor);
+  }
+
+  @Override
+  public String toString() {
+    return "Enum{" + "variantId=" + enumId + ", constructor='" + constructor + "'}";
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlGenMap.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlGenMap.java
new file mode 100644
index 0000000000..9a82711869
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlGenMap.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class DamlGenMap extends Value {
+
+  private final Map map;
+
+  private DamlGenMap(@NonNull Map<@NonNull Value, @NonNull Value> map) {
+    this.map = map;
+  }
+
+  /** The map that is passed to this constructor must not be changed once passed. */
+  static @NonNull DamlGenMap fromPrivateMap(@NonNull Map<@NonNull Value, @NonNull Value> map) {
+    return new DamlGenMap(Collections.unmodifiableMap(map));
+  }
+
+  public static DamlGenMap of(@NonNull Map<@NonNull Value, @NonNull Value> map) {
+    return fromPrivateMap(new LinkedHashMap<>(map));
+  }
+
+  public Stream> stream() {
+    return map.entrySet().stream();
+  }
+
+  public @NonNull  Map<@NonNull K, @NonNull V> toMap(
+      @NonNull Function<@NonNull Value, @NonNull K> keyMapper,
+      @NonNull Function<@NonNull Value, @NonNull V> valueMapper) {
+    return stream()
+        .collect(
+            Collectors.toMap(
+                e -> keyMapper.apply(e.getKey()),
+                e -> valueMapper.apply(e.getValue()),
+                (left, right) -> right,
+                LinkedHashMap::new));
+  }
+
+  public @NonNull  Map<@NonNull V, @NonNull V> toMap(
+      @NonNull Function<@NonNull Value, @NonNull V> valueMapper) {
+    return toMap(valueMapper, valueMapper);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    DamlGenMap other = (DamlGenMap) o;
+    return Objects.equals(map, other.map);
+  }
+
+  @Override
+  public int hashCode() {
+    return map.hashCode();
+  }
+
+  @Override
+  public @NonNull String toString() {
+    StringJoiner sj = new StringJoiner(", ", "GenMap{", "}");
+    map.forEach((key, value) -> sj.add(key.toString() + " -> " + value.toString()));
+    return sj.toString();
+  }
+
+  @Override
+  public ValueOuterClass.Value toProto() {
+    ValueOuterClass.GenMap.Builder mb = ValueOuterClass.GenMap.newBuilder();
+    map.forEach(
+        (key, value) ->
+            mb.addEntries(
+                ValueOuterClass.GenMap.Entry.newBuilder()
+                    .setKey(key.toProto())
+                    .setValue(value.toProto())));
+    return ValueOuterClass.Value.newBuilder().setGenMap(mb).build();
+  }
+
+  public static @NonNull DamlGenMap fromProto(ValueOuterClass.GenMap map) {
+    return map.getEntriesList().stream()
+        .collect(
+            DamlCollectors.toDamlGenMap(
+                entry -> fromProto(entry.getKey()), entry -> fromProto(entry.getValue())));
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlList.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlList.java
new file mode 100644
index 0000000000..cea4f1b419
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlList.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class DamlList extends Value {
+
+  private List values;
+
+  private DamlList() {}
+
+  /** The list that is passed to this constructor must not be change once passed. */
+  static @NonNull DamlList fromPrivateList(@NonNull List<@NonNull Value> values) {
+    DamlList damlList = new DamlList();
+    damlList.values = Collections.unmodifiableList(values);
+    return damlList;
+  }
+
+  public static DamlList of(@NonNull List<@NonNull Value> values) {
+    return fromPrivateList(new ArrayList<>(values));
+  }
+
+  public static DamlList of(@NonNull Value... values) {
+    return fromPrivateList(Arrays.asList(values));
+  }
+
+  @Deprecated // use DamlList:of
+  public DamlList(@NonNull List<@NonNull Value> values) {
+    this.values = values;
+  }
+
+  @Deprecated // use DamlMap:of
+  public DamlList(@NonNull Value... values) {
+    this(Arrays.asList(values));
+  }
+
+  @Deprecated // use DamlMap::stream or DamlMap::toListf
+  public @NonNull List<@NonNull Value> getValues() {
+    return toList(Function.identity());
+  }
+
+  public @NonNull Stream stream() {
+    return values.stream();
+  }
+
+  public @NonNull  List toList(Function valueMapper) {
+    return stream().map(valueMapper).collect(Collectors.toList());
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    DamlList list = (DamlList) o;
+    return Objects.equals(values, list.values);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(values);
+  }
+
+  @Override
+  public String toString() {
+    return "DamlList{" + "values=" + values + '}';
+  }
+
+  @Override
+  public ValueOuterClass.Value toProto() {
+    ValueOuterClass.List.Builder builder = ValueOuterClass.List.newBuilder();
+    for (Value value : this.values) {
+      builder.addElements(value.toProto());
+    }
+    return ValueOuterClass.Value.newBuilder().setList(builder.build()).build();
+  }
+
+  public static @NonNull DamlList fromProto(ValueOuterClass.List list) {
+    return list.getElementsList().stream().collect(DamlCollectors.toDamlList(Value::fromProto));
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlMap.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlMap.java
new file mode 100644
index 0000000000..da13834682
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlMap.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import java.util.*;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+// FIXME When removing this after the deprecation period is over, make DamlTextMap final
+/** @deprecated Use {@link DamlTextMap} instead. */
+@Deprecated
+public final class DamlMap extends DamlTextMap {
+
+  public DamlMap(Map<@NonNull String, @NonNull Value> value) {
+    super(Collections.unmodifiableMap(new HashMap<>(value)));
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlOptional.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlOptional.java
new file mode 100644
index 0000000000..58c6692a57
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlOptional.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.util.*;
+import java.util.function.Function;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class DamlOptional extends Value {
+
+  public static DamlOptional EMPTY = new DamlOptional(Optional.empty());
+
+  private final Value value;
+
+  DamlOptional(Value value) {
+    this.value = value;
+  }
+
+  @Deprecated // use DamlOptional.of
+  public DamlOptional(Optional<@NonNull Value> value) {
+    this(value.orElse(null));
+  }
+
+  public static DamlOptional of(@NonNull Optional<@NonNull Value> value) {
+    if (value.isPresent()) return new DamlOptional((value.get()));
+    else return EMPTY;
+  }
+
+  public static DamlOptional of(Value value) {
+    return new DamlOptional(value);
+  }
+
+  public java.util.Optional getValue() {
+    return java.util.Optional.ofNullable(value);
+  }
+
+  public @NonNull  Optional toOptional(Function<@NonNull Value, @NonNull V> valueMapper) {
+    return (value == null) ? Optional.empty() : Optional.of(valueMapper.apply(value));
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    DamlOptional optional = (DamlOptional) o;
+    return Objects.equals(value, optional.value);
+  }
+
+  public boolean isEmpty() {
+    return value == null;
+  }
+
+  @Override
+  public int hashCode() {
+    return (value == null) ? 0 : value.hashCode();
+  }
+
+  @Override
+  public @NonNull String toString() {
+    return "Optional{" + "value=" + value + '}';
+  }
+
+  @Deprecated // use DamlOptional::EMPTY
+  public static @NonNull DamlOptional empty() {
+    return EMPTY;
+  }
+
+  @Override
+  public ValueOuterClass.Value toProto() {
+    ValueOuterClass.Optional.Builder ob = ValueOuterClass.Optional.newBuilder();
+    if (value != null) ob.setValue(value.toProto());
+    return ValueOuterClass.Value.newBuilder().setOptional(ob.build()).build();
+  }
+
+  public static DamlOptional fromProto(ValueOuterClass.Optional optional) {
+    return (optional.hasValue()) ? new DamlOptional(fromProto(optional.getValue())) : EMPTY;
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlRecord.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlRecord.java
new file mode 100644
index 0000000000..6c665b074f
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlRecord.java
@@ -0,0 +1,181 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.util.*;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public class DamlRecord extends Value {
+
+  private final Optional recordId;
+
+  private final Map fieldsMap;
+
+  private final List fields;
+
+  public DamlRecord(@NonNull Identifier recordId, @NonNull Field... fields) {
+    this(recordId, Arrays.asList(fields));
+  }
+
+  public DamlRecord(@NonNull Field... fields) {
+    this(Arrays.asList(fields));
+  }
+
+  public DamlRecord(@NonNull Identifier recordId, @NonNull List<@NonNull Field> fields) {
+    this(Optional.of(recordId), fields, fieldsListToHashMap(fields));
+  }
+
+  public DamlRecord(@NonNull List<@NonNull Field> fields) {
+    this(Optional.empty(), fields, fieldsListToHashMap(fields));
+  }
+
+  public DamlRecord(
+      @NonNull Optional recordId,
+      @NonNull List<@NonNull Field> fields,
+      Map fieldsMap) {
+    this.recordId = recordId;
+    this.fields = fields;
+    this.fieldsMap = fieldsMap;
+  }
+
+  private static Map fieldsListToHashMap(@NonNull List<@NonNull Field> fields) {
+    if (fields.isEmpty() || !fields.get(0).getLabel().isPresent()) {
+      return Collections.emptyMap();
+    } else {
+      HashMap fieldsMap = new HashMap<>(fields.size());
+      for (Field field : fields) {
+        fieldsMap.put(field.getLabel().get(), field.getValue());
+      }
+      return fieldsMap;
+    }
+  }
+
+  @NonNull
+  public static DamlRecord fromProto(ValueOuterClass.Record record) {
+    ArrayList fields = new ArrayList<>(record.getFieldsCount());
+    HashMap fieldsMap = new HashMap<>(record.getFieldsCount());
+    for (ValueOuterClass.RecordField recordField : record.getFieldsList()) {
+      Field field = Field.fromProto(recordField);
+      fields.add(field);
+      if (field.getLabel().isPresent()) {
+        fieldsMap.put(field.getLabel().get(), field.getValue());
+      }
+    }
+    if (record.hasRecordId()) {
+      Identifier recordId = Identifier.fromProto(record.getRecordId());
+      return new DamlRecord(Optional.of(recordId), fields, fieldsMap);
+    } else {
+      return new DamlRecord(Optional.empty(), fields, fieldsMap);
+    }
+  }
+
+  @Override
+  public ValueOuterClass.Value toProto() {
+    return ValueOuterClass.Value.newBuilder().setRecord(this.toProtoRecord()).build();
+  }
+
+  public ValueOuterClass.Record toProtoRecord() {
+    ValueOuterClass.Record.Builder recordBuilder = ValueOuterClass.Record.newBuilder();
+    this.recordId.ifPresent(recordId -> recordBuilder.setRecordId(recordId.toProto()));
+    for (Field field : this.fields) {
+      recordBuilder.addFields(field.toProto());
+    }
+    return recordBuilder.build();
+  }
+
+  @NonNull
+  public Optional getRecordId() {
+    return recordId;
+  }
+
+  @NonNull
+  public List getFields() {
+    return fields;
+  }
+
+  /** @return the Map of this DamlRecord fields containing the records that have the label */
+  @NonNull
+  public Map<@NonNull String, @NonNull Value> getFieldsMap() {
+    return fieldsMap;
+  }
+
+  @Override
+  public String toString() {
+    return "DamlRecord{" + "recordId=" + recordId + ", fields=" + fields + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    DamlRecord record = (DamlRecord) o;
+    return Objects.equals(recordId, record.recordId) && Objects.equals(fields, record.fields);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(recordId, fields);
+  }
+
+  public static class Field {
+
+    private final Optional label;
+
+    private final Value value;
+
+    public Field(@NonNull String label, @NonNull Value value) {
+      this.label = Optional.of(label);
+      this.value = value;
+    }
+
+    public Field(@NonNull Value value) {
+      this.label = Optional.empty();
+      this.value = value;
+    }
+
+    @NonNull
+    public Optional getLabel() {
+      return label;
+    }
+
+    @NonNull
+    public Value getValue() {
+      return value;
+    }
+
+    public static Field fromProto(ValueOuterClass.RecordField field) {
+      String label = field.getLabel();
+      Value value = Value.fromProto(field.getValue());
+      return label.isEmpty() ? new Field(value) : new Field(label, value);
+    }
+
+    public ValueOuterClass.RecordField toProto() {
+      ValueOuterClass.RecordField.Builder builder = ValueOuterClass.RecordField.newBuilder();
+      this.label.ifPresent(builder::setLabel);
+      builder.setValue(this.value.toProto());
+      return builder.build();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+      Field field = (Field) o;
+      return Objects.equals(label, field.label) && Objects.equals(value, field.value);
+    }
+
+    @Override
+    public int hashCode() {
+
+      return Objects.hash(label, value);
+    }
+
+    @Override
+    public String toString() {
+      return "Field{" + "label=" + label + ", value=" + value + '}';
+    }
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlTextMap.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlTextMap.java
new file mode 100644
index 0000000000..092723f9b2
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DamlTextMap.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public class DamlTextMap extends Value {
+
+  private final Map map;
+
+  DamlTextMap(Map map) {
+    this.map = map;
+  }
+
+  /** The map that is passed to this constructor must not be changed once passed. */
+  static @NonNull DamlTextMap fromPrivateMap(Map<@NonNull String, @NonNull Value> value) {
+    return new DamlTextMap(Collections.unmodifiableMap(value));
+  }
+
+  private static @NonNull DamlTextMap EMPTY = fromPrivateMap(Collections.emptyMap());
+
+  public static DamlTextMap of(@NonNull Map<@NonNull String, @NonNull Value> value) {
+    return fromPrivateMap(new HashMap<>(value));
+  }
+
+  @Deprecated // use DamlTextMap::toMap or DamlTextMap::stream
+  public final @NonNull Map<@NonNull String, @NonNull Value> getMap() {
+    return toMap(Function.identity());
+  }
+
+  public Stream> stream() {
+    return map.entrySet().stream();
+  }
+
+  public final @NonNull  Map toMap(
+      @NonNull Function<@NonNull String, @NonNull K> keyMapper,
+      @NonNull Function<@NonNull Value, @NonNull V> valueMapper) {
+    return stream()
+        .collect(
+            Collectors.toMap(
+                e -> keyMapper.apply(e.getKey()), e -> valueMapper.apply(e.getValue())));
+  }
+
+  public final @NonNull  Map<@NonNull String, @NonNull V> toMap(
+      @NonNull Function<@NonNull Value, @NonNull V> valueMapper) {
+    return toMap(Function.identity(), valueMapper);
+  }
+
+  @Override
+  public final boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    DamlTextMap other = (DamlTextMap) o;
+    return Objects.equals(map, other.map);
+  }
+
+  @Override
+  public final int hashCode() {
+    return map.hashCode();
+  }
+
+  @Override
+  public final @NonNull String toString() {
+    StringJoiner sj = new StringJoiner(", ", "TextMap{", "}");
+    map.forEach((key, value) -> sj.add(key + "->" + value.toString()));
+    return sj.toString();
+  }
+
+  @Override
+  public final ValueOuterClass.Value toProto() {
+    ValueOuterClass.Map.Builder mb = ValueOuterClass.Map.newBuilder();
+    map.forEach(
+        (k, v) ->
+            mb.addEntries(ValueOuterClass.Map.Entry.newBuilder().setKey(k).setValue(v.toProto())));
+    return ValueOuterClass.Value.newBuilder().setMap(mb).build();
+  }
+
+  public static @NonNull DamlTextMap fromProto(ValueOuterClass.Map map) {
+    return map.getEntriesList().stream()
+        .collect(
+            DamlCollectors.toDamlTextMap(
+                ValueOuterClass.Map.Entry::getKey, entry -> fromProto(entry.getValue())));
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Date.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Date.java
new file mode 100644
index 0000000000..c46d9ef1df
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Date.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.time.LocalDate;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class Date extends Value {
+
+  private final LocalDate value;
+
+  public Date(int value) {
+    this.value = LocalDate.ofEpochDay(value);
+  }
+
+  @Override
+  public ValueOuterClass.Value toProto() {
+    return ValueOuterClass.Value.newBuilder().setDate((int) this.value.toEpochDay()).build();
+  }
+
+  @NonNull
+  public LocalDate getValue() {
+    return value;
+  }
+
+  @Override
+  public String toString() {
+    return "Date{" + "value=" + value + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    Date date = (Date) o;
+    return Objects.equals(value, date.value);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(value);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Decimal.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Decimal.java
new file mode 100644
index 0000000000..3cda2d15de
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Decimal.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import java.math.BigDecimal;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+// FIXME When removing this after the deprecation period is over, make Numeric final
+/** @deprecated Use {@link Numeric} instead. */
+@Deprecated
+public final class Decimal extends Numeric {
+  public Decimal(@NonNull BigDecimal value) {
+    super(value);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DeleteUserRequest.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DeleteUserRequest.java
new file mode 100644
index 0000000000..025128fbbf
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DeleteUserRequest.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class DeleteUserRequest {
+
+  private final String userId;
+
+  public DeleteUserRequest(@NonNull String userId) {
+    this.userId = userId;
+  }
+
+  public String getId() {
+    return userId;
+  }
+
+  @Override
+  public String toString() {
+    return "DeleteUserRequest{" + "userId='" + userId + '\'' + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    DeleteUserRequest that = (DeleteUserRequest) o;
+    return Objects.equals(userId, that.userId);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(userId);
+  }
+
+  public UserManagementServiceOuterClass.DeleteUserRequest toProto() {
+    return UserManagementServiceOuterClass.DeleteUserRequest.newBuilder()
+        .setUserId(this.userId)
+        .build();
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DeleteUserResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DeleteUserResponse.java
new file mode 100644
index 0000000000..b411d04067
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DeleteUserResponse.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+
+public final class DeleteUserResponse {
+
+  private static final DeleteUserResponse INSTANCE = new DeleteUserResponse();
+
+  private DeleteUserResponse() {}
+
+  @Override
+  public String toString() {
+    return "DeleteUserResponse{}";
+  }
+
+  public static DeleteUserResponse fromProto(
+      UserManagementServiceOuterClass.DeleteUserResponse response) {
+    // As this is so far a singleton, we just ignore the response
+    return INSTANCE;
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DisclosedContract.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DisclosedContract.java
new file mode 100644
index 0000000000..032d1e2096
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/DisclosedContract.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.CommandsOuterClass;
+import com.google.protobuf.ByteString;
+
+public final class DisclosedContract {
+  public final Identifier templateId;
+  public final String contractId;
+  public final ByteString createdEventBlob;
+
+  public DisclosedContract(Identifier templateId, String contractId, ByteString createdEventBlob) {
+    this.templateId = templateId;
+    this.contractId = contractId;
+    this.createdEventBlob = createdEventBlob;
+  }
+
+  public CommandsOuterClass.DisclosedContract toProto() {
+    return CommandsOuterClass.DisclosedContract.newBuilder()
+        .setTemplateId(this.templateId.toProto())
+        .setContractId(this.contractId)
+        .setCreatedEventBlob(this.createdEventBlob)
+        .build();
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Event.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Event.java
new file mode 100644
index 0000000000..bf7b81de11
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Event.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.EventOuterClass;
+import java.util.List;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+/**
+ * This interface represents events in {@link Transaction}s.
+ *
+ * @see CreatedEvent
+ * @see ArchivedEvent
+ * @see Transaction
+ */
+public interface Event {
+
+  @NonNull
+  List<@NonNull String> getWitnessParties();
+
+  @NonNull
+  String getEventId();
+
+  @NonNull
+  Identifier getTemplateId();
+
+  @NonNull
+  String getContractId();
+
+  default EventOuterClass.Event toProtoEvent() {
+    EventOuterClass.Event.Builder eventBuilder = EventOuterClass.Event.newBuilder();
+    if (this instanceof ArchivedEvent) {
+      ArchivedEvent event = (ArchivedEvent) this;
+      eventBuilder.setArchived(event.toProto());
+    } else if (this instanceof CreatedEvent) {
+      CreatedEvent event = (CreatedEvent) this;
+      eventBuilder.setCreated(event.toProto());
+    } else {
+      throw new RuntimeException(
+          "this should be ArchivedEvent or CreatedEvent or ExercisedEvent, found "
+              + this.toString());
+    }
+    return eventBuilder.build();
+  }
+
+  static Event fromProtoEvent(EventOuterClass.Event event) {
+    if (event.hasCreated()) {
+      return CreatedEvent.fromProto(event.getCreated());
+    } else if (event.hasArchived()) {
+      return ArchivedEvent.fromProto(event.getArchived());
+    } else {
+      throw new UnsupportedEventTypeException(event.toString());
+    }
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/EventUtils.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/EventUtils.java
new file mode 100644
index 0000000000..cdb48f0a28
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/EventUtils.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import java.util.List;
+
+public class EventUtils {
+
+  private EventUtils() {}
+
+  /** @hidden */
+  public static CreatedEvent singleCreatedEvent(List events) {
+    if (events.size() == 1 && events.get(0) instanceof CreatedEvent)
+      return (CreatedEvent) events.get(0);
+    throw new IllegalArgumentException(
+        "Expected exactly one created event from the transaction, got: " + events);
+  }
+
+  /** @hidden */
+  public static ExercisedEvent firstExercisedEvent(TransactionTree txTree) {
+    var maybeExercisedEvent =
+        txTree.getRootEventIds().stream()
+            .map(eventId -> txTree.getEventsById().get(eventId))
+            .filter(e -> e instanceof ExercisedEvent)
+            .map(e -> (ExercisedEvent) e)
+            .findFirst();
+
+    return maybeExercisedEvent.orElseThrow(
+        () ->
+            new IllegalArgumentException("Expect an exercised event but not found. tx: " + txTree));
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ExerciseByKeyCommand.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ExerciseByKeyCommand.java
new file mode 100644
index 0000000000..7fb49702b1
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ExerciseByKeyCommand.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.CommandsOuterClass;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class ExerciseByKeyCommand extends Command {
+
+  private final Identifier templateId;
+
+  private final Value contractKey;
+
+  private final String choice;
+
+  private final Value choiceArgument;
+
+  public ExerciseByKeyCommand(
+      @NonNull Identifier templateId,
+      @NonNull Value contractKey,
+      @NonNull String choice,
+      @NonNull Value choiceArgument) {
+    this.templateId = templateId;
+    this.contractKey = contractKey;
+    this.choice = choice;
+    this.choiceArgument = choiceArgument;
+  }
+
+  public static ExerciseByKeyCommand fromProto(CommandsOuterClass.ExerciseByKeyCommand command) {
+    Identifier templateId = Identifier.fromProto(command.getTemplateId());
+    Value contractKey = Value.fromProto(command.getContractKey());
+    String choice = command.getChoice();
+    Value choiceArgument = Value.fromProto(command.getChoiceArgument());
+    return new ExerciseByKeyCommand(templateId, contractKey, choice, choiceArgument);
+  }
+
+  public CommandsOuterClass.ExerciseByKeyCommand toProto() {
+    return CommandsOuterClass.ExerciseByKeyCommand.newBuilder()
+        .setTemplateId(this.templateId.toProto())
+        .setContractKey(this.contractKey.toProto())
+        .setChoice(this.choice)
+        .setChoiceArgument(this.choiceArgument.toProto())
+        .build();
+  }
+
+  @NonNull
+  @Override
+  public Identifier getTemplateId() {
+    return templateId;
+  }
+
+  @NonNull
+  public Value getContractKey() {
+    return contractKey;
+  }
+
+  @NonNull
+  public String getChoice() {
+    return choice;
+  }
+
+  @NonNull
+  public Value getChoiceArgument() {
+    return choiceArgument;
+  }
+
+  @Override
+  public String toString() {
+    return "ExerciseByKeyCommand{"
+        + "templateId="
+        + templateId
+        + ", contractKey='"
+        + contractKey
+        + '\''
+        + ", choice='"
+        + choice
+        + '\''
+        + ", choiceArgument="
+        + choiceArgument
+        + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ExerciseByKeyCommand that = (ExerciseByKeyCommand) o;
+    return Objects.equals(templateId, that.templateId)
+        && Objects.equals(contractKey, that.contractKey)
+        && Objects.equals(choice, that.choice)
+        && Objects.equals(choiceArgument, that.choiceArgument);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(templateId, contractKey, choice, choiceArgument);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ExerciseCommand.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ExerciseCommand.java
new file mode 100644
index 0000000000..447f423d9d
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ExerciseCommand.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.CommandsOuterClass;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class ExerciseCommand extends Command {
+
+  private final Identifier templateId;
+
+  private final String contractId;
+
+  private final String choice;
+
+  private final Value choiceArgument;
+
+  public ExerciseCommand(
+      @NonNull Identifier templateId,
+      @NonNull String contractId,
+      @NonNull String choice,
+      @NonNull Value choiceArgument) {
+    this.templateId = templateId;
+    this.contractId = contractId;
+    this.choice = choice;
+    this.choiceArgument = choiceArgument;
+  }
+
+  public static ExerciseCommand fromProto(CommandsOuterClass.ExerciseCommand command) {
+    Identifier templateId = Identifier.fromProto(command.getTemplateId());
+    String contractId = command.getContractId();
+    String choice = command.getChoice();
+    Value choiceArgument = Value.fromProto(command.getChoiceArgument());
+    return new ExerciseCommand(templateId, contractId, choice, choiceArgument);
+  }
+
+  public CommandsOuterClass.ExerciseCommand toProto() {
+    return CommandsOuterClass.ExerciseCommand.newBuilder()
+        .setTemplateId(this.templateId.toProto())
+        .setContractId(this.contractId)
+        .setChoice(this.choice)
+        .setChoiceArgument(this.choiceArgument.toProto())
+        .build();
+  }
+
+  @NonNull
+  @Override
+  public Identifier getTemplateId() {
+    return templateId;
+  }
+
+  @NonNull
+  public String getContractId() {
+    return contractId;
+  }
+
+  @NonNull
+  public String getChoice() {
+    return choice;
+  }
+
+  @NonNull
+  public Value getChoiceArgument() {
+    return choiceArgument;
+  }
+
+  @Override
+  public String toString() {
+    return "ExerciseCommand{"
+        + "templateId="
+        + templateId
+        + ", contractId='"
+        + contractId
+        + '\''
+        + ", choice='"
+        + choice
+        + '\''
+        + ", choiceArgument="
+        + choiceArgument
+        + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ExerciseCommand that = (ExerciseCommand) o;
+    return Objects.equals(templateId, that.templateId)
+        && Objects.equals(contractId, that.contractId)
+        && Objects.equals(choice, that.choice)
+        && Objects.equals(choiceArgument, that.choiceArgument);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(templateId, contractId, choice, choiceArgument);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ExercisedEvent.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ExercisedEvent.java
new file mode 100644
index 0000000000..0a81d979dc
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ExercisedEvent.java
@@ -0,0 +1,216 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.EventOuterClass;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class ExercisedEvent implements TreeEvent {
+
+  private final List witnessParties;
+
+  private final String eventId;
+
+  private final Identifier templateId;
+
+  private final Optional interfaceId;
+
+  private final String contractId;
+
+  private final String choice;
+
+  private final Value choiceArgument;
+
+  private final java.util.List actingParties;
+
+  private final boolean consuming;
+
+  private final List childEventIds;
+
+  private final Value exerciseResult;
+
+  public ExercisedEvent(
+      @NonNull List<@NonNull String> witnessParties,
+      @NonNull String eventId,
+      @NonNull Identifier templateId,
+      @NonNull Optional interfaceId,
+      @NonNull String contractId,
+      @NonNull String choice,
+      @NonNull Value choiceArgument,
+      @NonNull List<@NonNull String> actingParties,
+      boolean consuming,
+      @NonNull List<@NonNull String> childEventIds,
+      @NonNull Value exerciseResult) {
+    this.witnessParties = witnessParties;
+    this.eventId = eventId;
+    this.templateId = templateId;
+    this.interfaceId = interfaceId;
+    this.contractId = contractId;
+    this.choice = choice;
+    this.choiceArgument = choiceArgument;
+    this.actingParties = actingParties;
+    this.consuming = consuming;
+    this.childEventIds = childEventIds;
+    this.exerciseResult = exerciseResult;
+  }
+
+  @NonNull
+  @Override
+  public List<@NonNull String> getWitnessParties() {
+    return witnessParties;
+  }
+
+  @NonNull
+  @Override
+  public String getEventId() {
+    return eventId;
+  }
+
+  @NonNull
+  @Override
+  public Identifier getTemplateId() {
+    return templateId;
+  }
+
+  @NonNull
+  public Optional getInterfaceId() {
+    return interfaceId;
+  }
+
+  @NonNull
+  @Override
+  public String getContractId() {
+    return contractId;
+  }
+
+  @NonNull
+  public String getChoice() {
+    return choice;
+  }
+
+  @NonNull
+  public List<@NonNull String> getChildEventIds() {
+    return childEventIds;
+  }
+
+  @NonNull
+  public Value getChoiceArgument() {
+    return choiceArgument;
+  }
+
+  public @NonNull List<@NonNull String> getActingParties() {
+    return actingParties;
+  }
+
+  public boolean isConsuming() {
+    return consuming;
+  }
+
+  @NonNull
+  public Value getExerciseResult() {
+    return exerciseResult;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ExercisedEvent that = (ExercisedEvent) o;
+    return consuming == that.consuming
+        && Objects.equals(witnessParties, that.witnessParties)
+        && Objects.equals(eventId, that.eventId)
+        && Objects.equals(templateId, that.templateId)
+        && Objects.equals(interfaceId, that.interfaceId)
+        && Objects.equals(contractId, that.contractId)
+        && Objects.equals(choice, that.choice)
+        && Objects.equals(choiceArgument, that.choiceArgument)
+        && Objects.equals(actingParties, that.actingParties)
+        && Objects.equals(childEventIds, that.childEventIds)
+        && Objects.equals(exerciseResult, that.exerciseResult);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(
+        witnessParties,
+        eventId,
+        templateId,
+        interfaceId,
+        contractId,
+        choice,
+        choiceArgument,
+        actingParties,
+        childEventIds,
+        consuming,
+        exerciseResult);
+  }
+
+  @Override
+  public String toString() {
+    return "ExercisedEvent{"
+        + "witnessParties="
+        + witnessParties
+        + ", eventId='"
+        + eventId
+        + '\''
+        + ", templateId="
+        + templateId
+        + ", interfaceId="
+        + interfaceId
+        + ", contractId='"
+        + contractId
+        + '\''
+        + ", choice='"
+        + choice
+        + '\''
+        + ", choiceArgument="
+        + choiceArgument
+        + ", actingParties="
+        + actingParties
+        + ", consuming="
+        + consuming
+        + ", childEventIds="
+        + childEventIds
+        + ", exerciseResult="
+        + exerciseResult
+        + '}';
+  }
+
+  public EventOuterClass.@NonNull ExercisedEvent toProto() {
+    EventOuterClass.ExercisedEvent.Builder builder = EventOuterClass.ExercisedEvent.newBuilder();
+    builder.setEventId(getEventId());
+    builder.setChoice(getChoice());
+    builder.setChoiceArgument(getChoiceArgument().toProto());
+    builder.setConsuming(isConsuming());
+    builder.setContractId(getContractId());
+    builder.setTemplateId(getTemplateId().toProto());
+    interfaceId.ifPresent(i -> builder.setInterfaceId(i.toProto()));
+    builder.addAllActingParties(getActingParties());
+    builder.addAllWitnessParties(getWitnessParties());
+    builder.addAllChildEventIds(getChildEventIds());
+    builder.setExerciseResult(getExerciseResult().toProto());
+    return builder.build();
+  }
+
+  public static ExercisedEvent fromProto(EventOuterClass.ExercisedEvent exercisedEvent) {
+    return new ExercisedEvent(
+        exercisedEvent.getWitnessPartiesList(),
+        exercisedEvent.getEventId(),
+        Identifier.fromProto(exercisedEvent.getTemplateId()),
+        exercisedEvent.hasInterfaceId()
+            ? Optional.of(Identifier.fromProto(exercisedEvent.getInterfaceId()))
+            : Optional.empty(),
+        exercisedEvent.getContractId(),
+        exercisedEvent.getChoice(),
+        Value.fromProto(exercisedEvent.getChoiceArgument()),
+        exercisedEvent.getActingPartiesList(),
+        exercisedEvent.getConsuming(),
+        exercisedEvent.getChildEventIdsList(),
+        Value.fromProto(exercisedEvent.getExerciseResult()));
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Filter.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Filter.java
new file mode 100644
index 0000000000..23cd4c2949
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Filter.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.TransactionFilterOuterClass;
+
+public abstract class Filter {
+
+  public static Filter fromProto(TransactionFilterOuterClass.Filters filters) {
+    if (filters.hasInclusive()) {
+      return InclusiveFilter.fromProto(filters.getInclusive());
+    } else {
+      return NoFilter.instance;
+    }
+  }
+
+  public abstract TransactionFilterOuterClass.Filters toProto();
+
+  /**
+   * Settings for including an interface in {@link InclusiveFilter}. There are four possible values:
+   * {@link #HIDE_VIEW_HIDE_CREATED_EVENT_BLOB} and {@link #INCLUDE_VIEW_HIDE_CREATED_EVENT_BLOB}
+   * and {@link #HIDE_VIEW_INCLUDE_CREATED_EVENT_BLOB} and {@link
+   * #INCLUDE_VIEW_INCLUDE_CREATED_EVENT_BLOB}.
+   */
+  public static enum Interface {
+    HIDE_VIEW_HIDE_CREATED_EVENT_BLOB(false, false),
+    INCLUDE_VIEW_HIDE_CREATED_EVENT_BLOB(true, false),
+    HIDE_VIEW_INCLUDE_CREATED_EVENT_BLOB(false, true),
+    INCLUDE_VIEW_INCLUDE_CREATED_EVENT_BLOB(true, true);
+
+    public final boolean includeInterfaceView;
+    public final boolean includeCreatedEventBlob;
+
+    Interface(boolean includeInterfaceView, boolean includeCreatedEventBlob) {
+      this.includeInterfaceView = includeInterfaceView;
+      this.includeCreatedEventBlob = includeCreatedEventBlob;
+    }
+
+    private static Interface includeInterfaceView(
+        boolean includeInterfaceView, boolean includeCreatedEventBlob) {
+      if (!includeInterfaceView && !includeCreatedEventBlob)
+        return HIDE_VIEW_HIDE_CREATED_EVENT_BLOB;
+      else if (includeInterfaceView && !includeCreatedEventBlob)
+        return INCLUDE_VIEW_HIDE_CREATED_EVENT_BLOB;
+      else if (!includeInterfaceView) return HIDE_VIEW_INCLUDE_CREATED_EVENT_BLOB;
+      else return INCLUDE_VIEW_INCLUDE_CREATED_EVENT_BLOB;
+    }
+
+    public TransactionFilterOuterClass.InterfaceFilter toProto(Identifier interfaceId) {
+      return TransactionFilterOuterClass.InterfaceFilter.newBuilder()
+          .setInterfaceId(interfaceId.toProto())
+          .setIncludeInterfaceView(includeInterfaceView)
+          .build();
+    }
+
+    static Interface fromProto(TransactionFilterOuterClass.InterfaceFilter proto) {
+      return includeInterfaceView(
+          proto.getIncludeInterfaceView(), proto.getIncludeCreatedEventBlob());
+    }
+
+    Interface merge(Interface other) {
+      return includeInterfaceView(
+          includeInterfaceView || other.includeInterfaceView,
+          includeCreatedEventBlob || other.includeCreatedEventBlob);
+    }
+  }
+
+  public static enum Template {
+    INCLUDE_CREATED_EVENT_BLOB(true),
+    HIDE_CREATED_EVENT_BLOB(false);
+    public final boolean includeCreatedEventBlob;
+
+    Template(boolean includeCreatedEventBlob) {
+      this.includeCreatedEventBlob = includeCreatedEventBlob;
+    }
+
+    private static Template includeCreatedEventBlob(boolean includeCreatedEventBlob) {
+      return includeCreatedEventBlob ? INCLUDE_CREATED_EVENT_BLOB : HIDE_CREATED_EVENT_BLOB;
+    }
+
+    public TransactionFilterOuterClass.TemplateFilter toProto(Identifier templateId) {
+      return TransactionFilterOuterClass.TemplateFilter.newBuilder()
+          .setTemplateId(templateId.toProto())
+          .setIncludeCreatedEventBlob(includeCreatedEventBlob)
+          .build();
+    }
+
+    static Template fromProto(TransactionFilterOuterClass.TemplateFilter proto) {
+      return includeCreatedEventBlob(proto.getIncludeCreatedEventBlob());
+    }
+
+    Template merge(Template other) {
+      return includeCreatedEventBlob(includeCreatedEventBlob || other.includeCreatedEventBlob);
+    }
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/FiltersByParty.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/FiltersByParty.java
new file mode 100644
index 0000000000..991061f49d
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/FiltersByParty.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.TransactionFilterOuterClass;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class FiltersByParty extends TransactionFilter {
+
+  private Map partyToFilters;
+
+  @Override
+  public Set getParties() {
+    return partyToFilters.keySet();
+  }
+
+  public FiltersByParty(@NonNull Map<@NonNull String, @NonNull Filter> partyToFilters) {
+    this.partyToFilters = partyToFilters;
+  }
+
+  @Override
+  public TransactionFilterOuterClass.TransactionFilter toProto() {
+    HashMap partyToFilters =
+        new HashMap<>(this.partyToFilters.size());
+    for (Map.Entry entry : this.partyToFilters.entrySet()) {
+      partyToFilters.put(entry.getKey(), entry.getValue().toProto());
+    }
+    return TransactionFilterOuterClass.TransactionFilter.newBuilder()
+        .putAllFiltersByParty(partyToFilters)
+        .build();
+  }
+
+  public static FiltersByParty fromProto(
+      TransactionFilterOuterClass.TransactionFilter transactionFilter) {
+    Map partyToFilters =
+        transactionFilter.getFiltersByPartyMap();
+    HashMap converted = new HashMap<>(partyToFilters.size());
+    for (Map.Entry entry : partyToFilters.entrySet()) {
+      converted.put(entry.getKey(), Filter.fromProto(entry.getValue()));
+    }
+    return new FiltersByParty(converted);
+  }
+
+  public Map getPartyToFilters() {
+    return partyToFilters;
+  }
+
+  @Override
+  public String toString() {
+    return "FiltersByParty{" + "partyToFilters=" + partyToFilters + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    FiltersByParty that = (FiltersByParty) o;
+    return Objects.equals(partyToFilters, that.partyToFilters);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(partyToFilters);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetActiveContractsRequest.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetActiveContractsRequest.java
new file mode 100644
index 0000000000..04243d1aba
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetActiveContractsRequest.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ActiveContractsServiceOuterClass;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class GetActiveContractsRequest {
+
+  private final String ledgerId;
+
+  private final TransactionFilter transactionFilter;
+
+  private final boolean verbose;
+
+  public GetActiveContractsRequest(
+      @NonNull String ledgerId,
+      @NonNull TransactionFilter transactionFilter,
+      @NonNull boolean verbose) {
+    this.ledgerId = ledgerId;
+    this.transactionFilter = transactionFilter;
+    this.verbose = verbose;
+  }
+
+  public static GetActiveContractsRequest fromProto(
+      ActiveContractsServiceOuterClass.GetActiveContractsRequest request) {
+    String ledgerId = request.getLedgerId();
+    TransactionFilter filters = TransactionFilter.fromProto(request.getFilter());
+    boolean verbose = request.getVerbose();
+    return new GetActiveContractsRequest(ledgerId, filters, verbose);
+  }
+
+  public ActiveContractsServiceOuterClass.GetActiveContractsRequest toProto() {
+    return ActiveContractsServiceOuterClass.GetActiveContractsRequest.newBuilder()
+        .setLedgerId(this.ledgerId)
+        .setFilter(this.transactionFilter.toProto())
+        .setVerbose(this.verbose)
+        .build();
+  }
+
+  @NonNull
+  public String getLedgerId() {
+    return ledgerId;
+  }
+
+  @NonNull
+  public TransactionFilter getTransactionFilter() {
+    return transactionFilter;
+  }
+
+  public boolean isVerbose() {
+    return verbose;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    GetActiveContractsRequest that = (GetActiveContractsRequest) o;
+    return verbose == that.verbose
+        && Objects.equals(ledgerId, that.ledgerId)
+        && Objects.equals(transactionFilter, that.transactionFilter);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(ledgerId, transactionFilter, verbose);
+  }
+
+  @Override
+  public String toString() {
+    return "GetActiveContractsRequest{"
+        + "ledgerId='"
+        + ledgerId
+        + '\''
+        + ", transactionFilter="
+        + transactionFilter
+        + ", verbose="
+        + verbose
+        + '}';
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetActiveContractsResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetActiveContractsResponse.java
new file mode 100644
index 0000000000..578ffdf35d
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetActiveContractsResponse.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ActiveContractsServiceOuterClass;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class GetActiveContractsResponse implements WorkflowEvent {
+
+  private final String offset;
+
+  private final java.util.List activeContracts;
+
+  private final String workflowId;
+
+  public GetActiveContractsResponse(
+      @NonNull String offset, @NonNull List activeContracts, String workflowId) {
+    this.offset = offset;
+    this.activeContracts = activeContracts;
+    this.workflowId = workflowId;
+  }
+
+  public static GetActiveContractsResponse fromProto(
+      ActiveContractsServiceOuterClass.GetActiveContractsResponse response) {
+    List events =
+        response.getActiveContractsList().stream()
+            .map(CreatedEvent::fromProto)
+            .collect(Collectors.toList());
+    return new GetActiveContractsResponse(response.getOffset(), events, response.getWorkflowId());
+  }
+
+  public ActiveContractsServiceOuterClass.GetActiveContractsResponse toProto() {
+    return ActiveContractsServiceOuterClass.GetActiveContractsResponse.newBuilder()
+        .setOffset(this.offset)
+        .addAllActiveContracts(
+            this.activeContracts.stream().map(CreatedEvent::toProto).collect(Collectors.toList()))
+        .setWorkflowId(this.workflowId)
+        .build();
+  }
+
+  @NonNull
+  public Optional getOffset() {
+    // Empty string indicates that the field is not present in the protobuf.
+    return Optional.of(offset).filter(off -> !offset.equals(""));
+  }
+
+  @NonNull
+  public List<@NonNull CreatedEvent> getCreatedEvents() {
+    return activeContracts;
+  }
+
+  @NonNull
+  public String getWorkflowId() {
+    return workflowId;
+  }
+
+  @Override
+  public String toString() {
+    return "GetActiveContractsResponse{"
+        + "offset='"
+        + offset
+        + '\''
+        + ", activeContracts="
+        + activeContracts
+        + ", workflowId="
+        + workflowId
+        + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    GetActiveContractsResponse that = (GetActiveContractsResponse) o;
+    return Objects.equals(offset, that.offset)
+        && Objects.equals(activeContracts, that.activeContracts)
+        && Objects.equals(workflowId, that.workflowId);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(offset, activeContracts, workflowId);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetEventsByContractIdResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetEventsByContractIdResponse.java
new file mode 100644
index 0000000000..e12e39a931
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetEventsByContractIdResponse.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import java.util.Optional;
+
+public final class GetEventsByContractIdResponse {
+  private final Optional createEvent;
+
+  private final Optional archiveEvent;
+
+  public GetEventsByContractIdResponse(
+      Optional createEvent, Optional archiveEvent) {
+    this.createEvent = createEvent;
+    this.archiveEvent = archiveEvent;
+  }
+
+  public Optional getCreateEvent() {
+    return createEvent;
+  }
+
+  public Optional getArchiveEvent() {
+    return archiveEvent;
+  }
+
+  public static GetEventsByContractIdResponse fromProto(
+      com.daml.ledger.api.v1.EventQueryServiceOuterClass.GetEventsByContractIdResponse response) {
+    return new GetEventsByContractIdResponse(
+        response.hasCreateEvent()
+            ? Optional.of(CreatedEvent.fromProto(response.getCreateEvent()))
+            : Optional.empty(),
+        response.hasArchiveEvent()
+            ? Optional.of(ArchivedEvent.fromProto(response.getArchiveEvent()))
+            : Optional.empty());
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetEventsByContractKeyResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetEventsByContractKeyResponse.java
new file mode 100644
index 0000000000..d56a61af97
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetEventsByContractKeyResponse.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import java.util.Optional;
+
+public final class GetEventsByContractKeyResponse {
+  private final Optional createEvent;
+  private final Optional archiveEvent;
+  private final Optional continuationToken;
+
+  public GetEventsByContractKeyResponse(
+      Optional createEvent,
+      Optional archiveEvent,
+      Optional continuationToken) {
+    this.createEvent = createEvent;
+    this.archiveEvent = archiveEvent;
+    this.continuationToken = continuationToken;
+  }
+
+  public Optional getCreateEvent() {
+    return createEvent;
+  }
+
+  public Optional getArchiveEvent() {
+    return archiveEvent;
+  }
+
+  public Optional getContinuationToken() {
+    return continuationToken;
+  }
+
+  public static GetEventsByContractKeyResponse fromProto(
+      com.daml.ledger.api.v1.EventQueryServiceOuterClass.GetEventsByContractKeyResponse response) {
+    return new GetEventsByContractKeyResponse(
+        response.hasCreateEvent()
+            ? Optional.of(CreatedEvent.fromProto(response.getCreateEvent()))
+            : Optional.empty(),
+        response.hasArchiveEvent()
+            ? Optional.of(ArchivedEvent.fromProto(response.getArchiveEvent()))
+            : Optional.empty(),
+        response.getContinuationToken().isEmpty()
+            ? Optional.of(response.getContinuationToken())
+            : Optional.empty());
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetFlatTransactionResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetFlatTransactionResponse.java
new file mode 100644
index 0000000000..8b913578c2
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetFlatTransactionResponse.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.TransactionServiceOuterClass;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class GetFlatTransactionResponse {
+
+  private final Transaction transaction;
+
+  public GetFlatTransactionResponse(@NonNull Transaction transaction) {
+    this.transaction = transaction;
+  }
+
+  public static GetFlatTransactionResponse fromProto(
+      TransactionServiceOuterClass.GetFlatTransactionResponse response) {
+    return new GetFlatTransactionResponse(Transaction.fromProto(response.getTransaction()));
+  }
+
+  public TransactionServiceOuterClass.GetFlatTransactionResponse toProto() {
+    return TransactionServiceOuterClass.GetFlatTransactionResponse.newBuilder()
+        .setTransaction(this.transaction.toProto())
+        .build();
+  }
+
+  public Transaction getTransaction() {
+    return transaction;
+  }
+
+  @Override
+  public String toString() {
+    return "GetFlatTransactionResponse{" + "transaction=" + transaction + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    GetFlatTransactionResponse that = (GetFlatTransactionResponse) o;
+    return Objects.equals(transaction, that.transaction);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(transaction);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetLedgerEndResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetLedgerEndResponse.java
new file mode 100644
index 0000000000..b157f28cfc
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetLedgerEndResponse.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.TransactionServiceOuterClass;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class GetLedgerEndResponse {
+
+  private final LedgerOffset offset;
+
+  public GetLedgerEndResponse(@NonNull LedgerOffset offset) {
+    this.offset = offset;
+  }
+
+  public static GetLedgerEndResponse fromProto(
+      TransactionServiceOuterClass.GetLedgerEndResponse response) {
+    return new GetLedgerEndResponse(LedgerOffset.fromProto(response.getOffset()));
+  }
+
+  public TransactionServiceOuterClass.GetLedgerEndResponse toProto() {
+    return TransactionServiceOuterClass.GetLedgerEndResponse.newBuilder()
+        .setOffset(this.offset.toProto())
+        .build();
+  }
+
+  @NonNull
+  public LedgerOffset getOffset() {
+    return offset;
+  }
+
+  @Override
+  public String toString() {
+    return "GetLedgerEndResponse{" + "offset=" + offset + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    GetLedgerEndResponse that = (GetLedgerEndResponse) o;
+    return Objects.equals(offset, that.offset);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(offset);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetPackageResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetPackageResponse.java
new file mode 100644
index 0000000000..b402ca8e5a
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetPackageResponse.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.google.protobuf.ByteString;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class GetPackageResponse {
+
+  // Clone of the PackageServiceOuterClass.HashFunction enumeration
+  public enum HashFunction {
+    SHA256(0),
+    UNRECOGNIZED(-1),
+    ;
+
+    private final int value;
+
+    private static Map valueToEnumMap =
+        EnumSet.allOf(GetPackageResponse.HashFunction.class).stream()
+            .collect(Collectors.toMap(e -> e.value, Function.identity()));
+
+    private HashFunction(int value) {
+      this.value = value;
+    }
+
+    public static GetPackageResponse.HashFunction valueOf(int value) {
+      return valueToEnumMap.getOrDefault(value, UNRECOGNIZED);
+    }
+  }
+
+  private final HashFunction hashFunction;
+  private final String hash;
+  private final ByteString archivePayload;
+
+  public GetPackageResponse(
+      HashFunction hashFunction, @NonNull String hash, @NonNull ByteString archivePayload) {
+    this.hashFunction = hashFunction;
+    this.hash = hash;
+    this.archivePayload = archivePayload;
+  }
+
+  public HashFunction getHashFunction() {
+    return hashFunction;
+  }
+
+  public String getHash() {
+    return hash;
+  }
+
+  public byte[] getArchivePayload() {
+    return archivePayload.toByteArray();
+  }
+
+  public static GetPackageResponse fromProto(
+      com.daml.ledger.api.v1.PackageServiceOuterClass.GetPackageResponse p) {
+    return new GetPackageResponse(
+        HashFunction.valueOf(p.getHashFunctionValue()), p.getHash(), p.getArchivePayload());
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetPackageStatusResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetPackageStatusResponse.java
new file mode 100644
index 0000000000..eb238eac1f
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetPackageStatusResponse.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public final class GetPackageStatusResponse {
+
+  // Clone of the PackageServiceOuterClass.PackageStatus enumeration
+  public enum PackageStatus {
+    UNKNOWN(0),
+    REGISTERED(1),
+    UNRECOGNIZED(-1),
+    ;
+
+    private final int value;
+
+    private static Map valueToEnumMap =
+        EnumSet.allOf(PackageStatus.class).stream()
+            .collect(Collectors.toMap(e -> e.value, Function.identity()));
+
+    private PackageStatus(int value) {
+      this.value = value;
+    }
+
+    public static PackageStatus valueOf(int value) {
+      return valueToEnumMap.getOrDefault(value, UNRECOGNIZED);
+    }
+  }
+
+  private final PackageStatus packageStatus;
+
+  public GetPackageStatusResponse(PackageStatus packageStatus) {
+    this.packageStatus = packageStatus;
+  }
+
+  public PackageStatus getPackageStatusValue() {
+    return packageStatus;
+  }
+
+  public static GetPackageStatusResponse fromProto(
+      com.daml.ledger.api.v1.PackageServiceOuterClass.GetPackageStatusResponse p) {
+    return new GetPackageStatusResponse(PackageStatus.valueOf(p.getPackageStatusValue()));
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetTransactionResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetTransactionResponse.java
new file mode 100644
index 0000000000..ba23e62af0
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetTransactionResponse.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.TransactionServiceOuterClass;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class GetTransactionResponse {
+
+  private final TransactionTree transaction;
+
+  public GetTransactionResponse(@NonNull TransactionTree transaction) {
+    this.transaction = transaction;
+  }
+
+  public static GetTransactionResponse fromProto(
+      TransactionServiceOuterClass.GetTransactionResponse response) {
+    return new GetTransactionResponse(TransactionTree.fromProto(response.getTransaction()));
+  }
+
+  public TransactionServiceOuterClass.GetTransactionResponse toProto() {
+    return TransactionServiceOuterClass.GetTransactionResponse.newBuilder()
+        .setTransaction(this.transaction.toProto())
+        .build();
+  }
+
+  public TransactionTree getTransaction() {
+    return transaction;
+  }
+
+  @Override
+  public String toString() {
+    return "GetTransactionResponse{" + "transaction=" + transaction + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    GetTransactionResponse that = (GetTransactionResponse) o;
+    return Objects.equals(transaction, that.transaction);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(transaction);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetTransactionTreesResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetTransactionTreesResponse.java
new file mode 100644
index 0000000000..604ad4f996
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetTransactionTreesResponse.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.TransactionOuterClass;
+import com.daml.ledger.api.v1.TransactionServiceOuterClass;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class GetTransactionTreesResponse {
+
+  private final List transactions;
+
+  public GetTransactionTreesResponse(@NonNull List<@NonNull TransactionTree> transactions) {
+    this.transactions = transactions;
+  }
+
+  public static GetTransactionTreesResponse fromProto(
+      TransactionServiceOuterClass.GetTransactionTreesResponse response) {
+    ArrayList transactionTrees = new ArrayList<>(response.getTransactionsCount());
+    for (TransactionOuterClass.TransactionTree transactionTree : response.getTransactionsList()) {
+      transactionTrees.add(TransactionTree.fromProto(transactionTree));
+    }
+    return new GetTransactionTreesResponse(transactionTrees);
+  }
+
+  public TransactionServiceOuterClass.GetTransactionTreesResponse toProto() {
+    ArrayList transactionTrees =
+        new ArrayList<>(this.transactions.size());
+    for (TransactionTree transactionTree : this.transactions) {
+      transactionTrees.add(transactionTree.toProto());
+    }
+    return TransactionServiceOuterClass.GetTransactionTreesResponse.newBuilder()
+        .addAllTransactions(transactionTrees)
+        .build();
+  }
+
+  @NonNull
+  public List<@NonNull TransactionTree> getTransactions() {
+    return transactions;
+  }
+
+  @Override
+  public String toString() {
+    return "GetTransactionTreesResponse{" + "transactions=" + transactions + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    GetTransactionTreesResponse that = (GetTransactionTreesResponse) o;
+    return Objects.equals(transactions, that.transactions);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(transactions);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetTransactionsRequest.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetTransactionsRequest.java
new file mode 100644
index 0000000000..63f8e61bbe
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetTransactionsRequest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.TransactionServiceOuterClass;
+import java.util.Optional;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class GetTransactionsRequest {
+
+  private final String ledgerId;
+
+  private final LedgerOffset begin;
+
+  private final Optional end;
+
+  private final TransactionFilter filter;
+
+  private final boolean verbose;
+
+  public GetTransactionsRequest(
+      @NonNull String ledgerId,
+      @NonNull LedgerOffset begin,
+      @NonNull LedgerOffset end,
+      @NonNull TransactionFilter filter,
+      boolean verbose) {
+    this.ledgerId = ledgerId;
+    this.begin = begin;
+    this.end = Optional.of(end);
+    this.filter = filter;
+    this.verbose = verbose;
+  }
+
+  public GetTransactionsRequest(
+      @NonNull String ledgerId,
+      @NonNull LedgerOffset begin,
+      @NonNull TransactionFilter filter,
+      boolean verbose) {
+    this.ledgerId = ledgerId;
+    this.begin = begin;
+    this.end = Optional.empty();
+    this.filter = filter;
+    this.verbose = verbose;
+  }
+
+  public static GetTransactionsRequest fromProto(
+      TransactionServiceOuterClass.GetTransactionsRequest request) {
+    String ledgerId = request.getLedgerId();
+    LedgerOffset begin = LedgerOffset.fromProto(request.getBegin());
+    TransactionFilter filter = TransactionFilter.fromProto(request.getFilter());
+    boolean verbose = request.getVerbose();
+    if (request.hasEnd()) {
+      LedgerOffset end = LedgerOffset.fromProto(request.getEnd());
+      return new GetTransactionsRequest(ledgerId, begin, end, filter, verbose);
+    } else {
+      return new GetTransactionsRequest(ledgerId, begin, filter, verbose);
+    }
+  }
+
+  public TransactionServiceOuterClass.GetTransactionsRequest toProto() {
+    TransactionServiceOuterClass.GetTransactionsRequest.Builder builder =
+        TransactionServiceOuterClass.GetTransactionsRequest.newBuilder();
+    builder.setLedgerId(this.ledgerId);
+    builder.setBegin(this.begin.toProto());
+    this.end.ifPresent(end -> builder.setEnd(end.toProto()));
+    builder.setFilter(this.filter.toProto());
+    builder.setVerbose(this.verbose);
+    return builder.build();
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetTransactionsResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetTransactionsResponse.java
new file mode 100644
index 0000000000..b0e730905a
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetTransactionsResponse.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.TransactionOuterClass;
+import com.daml.ledger.api.v1.TransactionServiceOuterClass;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class GetTransactionsResponse {
+
+  private final List transactions;
+
+  public GetTransactionsResponse(@NonNull List<@NonNull Transaction> transactions) {
+    this.transactions = transactions;
+  }
+
+  public static GetTransactionsResponse fromProto(
+      TransactionServiceOuterClass.GetTransactionsResponse response) {
+    ArrayList transactions = new ArrayList<>(response.getTransactionsCount());
+    for (TransactionOuterClass.Transaction transaction : response.getTransactionsList()) {
+      transactions.add(Transaction.fromProto(transaction));
+    }
+    return new GetTransactionsResponse(transactions);
+  }
+
+  public TransactionServiceOuterClass.GetTransactionsResponse toProto() {
+    ArrayList transactions =
+        new ArrayList<>(this.transactions.size());
+    for (Transaction transaction : this.transactions) {
+      transactions.add(transaction.toProto());
+    }
+    return TransactionServiceOuterClass.GetTransactionsResponse.newBuilder()
+        .addAllTransactions(transactions)
+        .build();
+  }
+
+  @NonNull
+  public List<@NonNull Transaction> getTransactions() {
+    return transactions;
+  }
+
+  @Override
+  public String toString() {
+    return "GetTransactionsResponse{" + "transactions=" + transactions + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    GetTransactionsResponse that = (GetTransactionsResponse) o;
+    return Objects.equals(transactions, that.transactions);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(transactions);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetUserRequest.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetUserRequest.java
new file mode 100644
index 0000000000..91a6fcbb0b
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetUserRequest.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class GetUserRequest {
+
+  private final String userId;
+
+  public GetUserRequest(@NonNull String userId) {
+    this.userId = userId;
+  }
+
+  public String getId() {
+    return userId;
+  }
+
+  @Override
+  public String toString() {
+    return "GetUserRequest{" + "userId='" + userId + '\'' + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    GetUserRequest that = (GetUserRequest) o;
+    return Objects.equals(userId, that.userId);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(userId);
+  }
+
+  public UserManagementServiceOuterClass.GetUserRequest toProto() {
+    return UserManagementServiceOuterClass.GetUserRequest.newBuilder()
+        .setUserId(this.userId)
+        .build();
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetUserResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetUserResponse.java
new file mode 100644
index 0000000000..aa3b18794a
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GetUserResponse.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+import java.util.Objects;
+
+public final class GetUserResponse {
+
+  private final User user;
+
+  public GetUserResponse(User user) {
+    this.user = user;
+  }
+
+  public User getUser() {
+    return user;
+  }
+
+  public static GetUserResponse fromProto(UserManagementServiceOuterClass.GetUserResponse proto) {
+    return new GetUserResponse(User.fromProto(proto.getUser()));
+  }
+
+  public UserManagementServiceOuterClass.GetUserResponse toProto() {
+    return UserManagementServiceOuterClass.GetUserResponse.newBuilder()
+        .setUser(this.user.toProto())
+        .build();
+  }
+
+  @Override
+  public String toString() {
+    return "GetUserResponse{" + "user=" + user + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    GetUserResponse that = (GetUserResponse) o;
+    return user.equals(that.user);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(user);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GrantUserRightsRequest.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GrantUserRightsRequest.java
new file mode 100644
index 0000000000..21d24f4574
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GrantUserRightsRequest.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public final class GrantUserRightsRequest {
+
+  private final String userId;
+  private final List rights;
+
+  public GrantUserRightsRequest(String userId, User.Right right, User.Right... rights) {
+    this.userId = userId;
+    this.rights = new ArrayList<>(rights.length + 1);
+    this.rights.add(right);
+    this.rights.addAll(Arrays.asList(rights));
+  }
+
+  public String getUserId() {
+    return userId;
+  }
+
+  public List getRights() {
+    return new ArrayList<>(rights);
+  }
+
+  public UserManagementServiceOuterClass.GrantUserRightsRequest toProto() {
+    return UserManagementServiceOuterClass.GrantUserRightsRequest.newBuilder()
+        .setUserId(this.userId)
+        .addAllRights(this.rights.stream().map(User.Right::toProto).collect(Collectors.toList()))
+        .build();
+  }
+
+  @Override
+  public String toString() {
+    return "GrantUserRightsRequest{" + "userId=" + userId + ", rights=" + rights + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    GrantUserRightsRequest that = (GrantUserRightsRequest) o;
+    return userId.equals(that.userId) && rights.equals(that.rights);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(userId, rights);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GrantUserRightsResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GrantUserRightsResponse.java
new file mode 100644
index 0000000000..af899a9282
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/GrantUserRightsResponse.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class GrantUserRightsResponse {
+
+  private final List newlyGrantedRights;
+
+  public GrantUserRightsResponse(@NonNull List newlyGrantedRights) {
+    this.newlyGrantedRights = new ArrayList<>(newlyGrantedRights);
+  }
+
+  public List getNewlyGrantedRights() {
+    return new ArrayList<>(this.newlyGrantedRights);
+  }
+
+  public static GrantUserRightsResponse fromProto(
+      UserManagementServiceOuterClass.GrantUserRightsResponse proto) {
+    return new GrantUserRightsResponse(
+        proto.getNewlyGrantedRightsList().stream()
+            .map(User.Right::fromProto)
+            .collect(Collectors.toList()));
+  }
+
+  @Override
+  public String toString() {
+    return "GrantUserRightsResponse{" + "newlyGrantedRights=" + newlyGrantedRights + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    GrantUserRightsResponse that = (GrantUserRightsResponse) o;
+    return Objects.equals(newlyGrantedRights, that.newlyGrantedRights);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(newlyGrantedRights);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Identifier.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Identifier.java
new file mode 100644
index 0000000000..8cc86fee3e
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Identifier.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class Identifier {
+
+  private final String packageId;
+  private final String moduleName;
+  private final String entityName;
+
+  /**
+   * This constructor is deprecated in favor of {@link Identifier#Identifier(String, String,
+   * String)}
+   */
+  @Deprecated
+  public Identifier(@NonNull String packageId, @NonNull String name) {
+    this.packageId = packageId;
+    int lastDot = name.lastIndexOf('.');
+    if (lastDot <= 0) {
+      // The module component of the name must be at least 1 character long.
+      // if no '.' is found or it is on the first position, then the name is not a valid identifier.
+      throw new IllegalArgumentException(
+          String.format(
+              "Identifier name [%s] has wrong format. Dot-separated module and entity name"
+                  + " expected (e.g.: Foo.Bar)",
+              name));
+    }
+    this.moduleName = name.substring(0, lastDot);
+    this.entityName = name.substring(lastDot + 1);
+  }
+
+  public Identifier(
+      @NonNull String packageId, @NonNull String moduleName, @NonNull String entityName) {
+    this.packageId = packageId;
+    this.moduleName = moduleName;
+    this.entityName = entityName;
+  }
+
+  @NonNull
+  public static Identifier fromProto(ValueOuterClass.Identifier identifier) {
+    if (!identifier.getModuleName().isEmpty() && !identifier.getEntityName().isEmpty()) {
+      return new Identifier(
+          identifier.getPackageId(), identifier.getModuleName(), identifier.getEntityName());
+    } else {
+      throw new IllegalArgumentException(
+          String.format(
+              "Invalid identifier [%s]: both module_name and entity_name must be set.",
+              identifier));
+    }
+  }
+
+  public ValueOuterClass.Identifier toProto() {
+    return ValueOuterClass.Identifier.newBuilder()
+        .setPackageId(this.packageId)
+        .setModuleName(this.moduleName)
+        .setEntityName(this.entityName)
+        .build();
+  }
+
+  @NonNull
+  public String getPackageId() {
+    return packageId;
+  }
+
+  @NonNull
+  @Deprecated
+  public String getName() {
+    return moduleName.concat(".").concat(entityName);
+  }
+
+  @NonNull
+  public String getModuleName() {
+    return moduleName;
+  }
+
+  @NonNull
+  public String getEntityName() {
+    return entityName;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    Identifier that = (Identifier) o;
+    return Objects.equals(packageId, that.packageId)
+        && Objects.equals(moduleName, that.moduleName)
+        && Objects.equals(entityName, that.entityName);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(packageId, moduleName, entityName);
+  }
+
+  @Override
+  public String toString() {
+    return "Identifier{"
+        + "packageId='"
+        + packageId
+        + '\''
+        + ", moduleName='"
+        + moduleName
+        + '\''
+        + ", entityName='"
+        + entityName
+        + '\''
+        + '}';
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/InclusiveFilter.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/InclusiveFilter.java
new file mode 100644
index 0000000000..c8e58a1d0f
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/InclusiveFilter.java
@@ -0,0 +1,156 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.TransactionFilterOuterClass;
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class InclusiveFilter extends Filter {
+
+  private Set templateIds;
+  private Map<@NonNull Identifier, Filter.@NonNull Interface> interfaceFilters;
+  private Map<@NonNull Identifier, Filter.@NonNull Template> templateFilters;
+
+  private InclusiveFilter(
+      @NonNull Set<@NonNull Identifier> templateIds,
+      @NonNull Map<@NonNull Identifier, Filter.@NonNull Interface> interfaceFilters,
+      @NonNull Map<@NonNull Identifier, Filter.@NonNull Template> templateFilters) {
+    this.templateIds = templateIds;
+    this.interfaceFilters = interfaceFilters;
+    this.templateFilters = templateFilters;
+  }
+
+  public InclusiveFilter(
+      @NonNull Map<@NonNull Identifier, Filter.@NonNull Interface> interfaceFilters,
+      @NonNull Map<@NonNull Identifier, Filter.@NonNull Template> templateFilters) {
+    this(Collections.emptySet(), interfaceFilters, templateFilters);
+  }
+
+  /**
+   * @deprecated Use {@link #ofTemplateIds} instead; {@code templateIds} must not include interface
+   *     IDs. Since Daml 2.4.0
+   */
+  @Deprecated
+  public InclusiveFilter(@NonNull Set<@NonNull Identifier> templateIds) {
+    this(templateIds, Collections.emptyMap());
+  }
+
+  /**
+   * @deprecated Use the constructor with {@link #templateFilters} instead of IDs. Since Daml 2.8.0
+   */
+  @Deprecated
+  public InclusiveFilter(
+      @NonNull Set<@NonNull Identifier> templateIds,
+      @NonNull Map<@NonNull Identifier, Filter.@NonNull Interface> interfaceIds) {
+    this(templateIds, interfaceIds, Collections.emptyMap());
+  }
+
+  public static InclusiveFilter ofTemplateIds(@NonNull Set<@NonNull Identifier> templateIds) {
+    return new InclusiveFilter(
+        Collections.emptyMap(),
+        templateIds.stream()
+            .collect(
+                Collectors.toUnmodifiableMap(
+                    Function.identity(), tId -> Template.HIDE_CREATED_EVENT_BLOB)));
+  }
+
+  @NonNull
+  public Set<@NonNull Identifier> getTemplateIds() {
+    return templateIds;
+  }
+
+  @NonNull
+  public Map<@NonNull Identifier, Filter.@NonNull Interface> getInterfaceFilters() {
+    return interfaceFilters;
+  }
+
+  @NonNull
+  public Map<@NonNull Identifier, Filter.@NonNull Template> getTemplateFilters() {
+    return templateFilters;
+  }
+
+  @SuppressWarnings("deprecation")
+  @Override
+  public TransactionFilterOuterClass.Filters toProto() {
+    ArrayList templateIds = new ArrayList<>(this.templateIds.size());
+    for (Identifier identifier : this.templateIds) {
+      templateIds.add(identifier.toProto());
+    }
+    TransactionFilterOuterClass.InclusiveFilters inclusiveFilter =
+        TransactionFilterOuterClass.InclusiveFilters.newBuilder()
+            .addAllTemplateIds(templateIds)
+            .addAllInterfaceFilters(
+                interfaceFilters.entrySet().stream()
+                    .map(idFilt -> idFilt.getValue().toProto(idFilt.getKey()))
+                    .collect(Collectors.toUnmodifiableList()))
+            .addAllTemplateFilters(
+                templateFilters.entrySet().stream()
+                    .map(
+                        templateFilter ->
+                            templateFilter.getValue().toProto(templateFilter.getKey()))
+                    .collect(Collectors.toUnmodifiableList()))
+            .build();
+    return TransactionFilterOuterClass.Filters.newBuilder().setInclusive(inclusiveFilter).build();
+  }
+
+  @SuppressWarnings("deprecation")
+  public static InclusiveFilter fromProto(
+      TransactionFilterOuterClass.InclusiveFilters inclusiveFilters) {
+    HashSet templateIds = new HashSet<>(inclusiveFilters.getTemplateIdsCount());
+    for (ValueOuterClass.Identifier templateId : inclusiveFilters.getTemplateIdsList()) {
+      templateIds.add(Identifier.fromProto(templateId));
+    }
+    var interfaceIds =
+        inclusiveFilters.getInterfaceFiltersList().stream()
+            .collect(
+                Collectors.toUnmodifiableMap(
+                    ifFilt -> Identifier.fromProto(ifFilt.getInterfaceId()),
+                    Filter.Interface::fromProto,
+                    Filter.Interface::merge));
+    var templateFilters =
+        inclusiveFilters.getTemplateFiltersList().stream()
+            .collect(
+                Collectors.toUnmodifiableMap(
+                    templateFilter -> Identifier.fromProto(templateFilter.getTemplateId()),
+                    Filter.Template::fromProto,
+                    Filter.Template::merge));
+    return new InclusiveFilter(templateIds, interfaceIds, templateFilters);
+  }
+
+  @Override
+  public String toString() {
+    return "InclusiveFilter{"
+        + "templateIds="
+        + templateIds
+        + ", interfaceFilters="
+        + interfaceFilters
+        + ", templateFilters="
+        + templateFilters
+        + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    InclusiveFilter that = (InclusiveFilter) o;
+    return Objects.equals(templateIds, that.templateIds)
+        && Objects.equals(interfaceFilters, that.interfaceFilters)
+        && Objects.equals(templateFilters, that.templateFilters);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(templateIds, interfaceFilters, templateFilters);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Int64.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Int64.java
new file mode 100644
index 0000000000..5de32e2358
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Int64.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.util.Objects;
+
+public final class Int64 extends Value {
+
+  private long value;
+
+  public Int64(long int64) {
+    this.value = int64;
+  }
+
+  public long getValue() {
+    return value;
+  }
+
+  @Override
+  public ValueOuterClass.Value toProto() {
+    return ValueOuterClass.Value.newBuilder().setInt64(this.value).build();
+  }
+
+  @Override
+  public String toString() {
+    return "Int64{" + "value=" + value + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    Int64 int64 = (Int64) o;
+    return value == int64.value;
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(value);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/LedgerOffset.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/LedgerOffset.java
new file mode 100644
index 0000000000..1f7849b013
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/LedgerOffset.java
@@ -0,0 +1,126 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.LedgerOffsetOuterClass;
+import java.util.Objects;
+
+public abstract class LedgerOffset {
+
+  public static final class LedgerBegin extends LedgerOffset {
+    static LedgerBegin instance = new LedgerBegin();
+
+    private LedgerBegin() {}
+
+    public static LedgerBegin getInstance() {
+      return instance;
+    }
+
+    @Override
+    public String toString() {
+      return "LedgerOffset.Begin";
+    }
+  }
+
+  public static final class LedgerEnd extends LedgerOffset {
+    static LedgerEnd instance = new LedgerEnd();
+
+    private LedgerEnd() {}
+
+    public static LedgerEnd getInstance() {
+      return instance;
+    }
+
+    @Override
+    public String toString() {
+      return "LedgerOffset.End";
+    }
+  }
+
+  public static final class Absolute extends LedgerOffset {
+    private final String offset;
+
+    public Absolute(String offset) {
+      this.offset = offset;
+    }
+
+    public String getOffset() {
+      return offset;
+    }
+
+    @Override
+    public String toString() {
+      return "LedgerOffset.Absolute(" + offset + ')';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+      Absolute absolute = (Absolute) o;
+      return Objects.equals(offset, absolute.offset);
+    }
+
+    @Override
+    public int hashCode() {
+
+      return Objects.hash(offset);
+    }
+  }
+
+  public static LedgerOffset fromProto(LedgerOffsetOuterClass.LedgerOffset ledgerOffset) {
+    switch (ledgerOffset.getValueCase()) {
+      case ABSOLUTE:
+        return new Absolute(ledgerOffset.getAbsolute());
+      case BOUNDARY:
+        switch (ledgerOffset.getBoundary()) {
+          case LEDGER_BEGIN:
+            return LedgerBegin.instance;
+          case LEDGER_END:
+            return LedgerEnd.instance;
+          case UNRECOGNIZED:
+          default:
+            throw new LedgerBoundaryUnrecognized(ledgerOffset.getBoundary());
+        }
+      case VALUE_NOT_SET:
+      default:
+        throw new LedgerBoundaryUnset(ledgerOffset);
+    }
+  }
+
+  public final LedgerOffsetOuterClass.LedgerOffset toProto() {
+    if (this instanceof LedgerBegin) {
+      return LedgerOffsetOuterClass.LedgerOffset.newBuilder()
+          .setBoundary(LedgerOffsetOuterClass.LedgerOffset.LedgerBoundary.LEDGER_BEGIN)
+          .build();
+    } else if (this instanceof LedgerEnd) {
+      return LedgerOffsetOuterClass.LedgerOffset.newBuilder()
+          .setBoundary(LedgerOffsetOuterClass.LedgerOffset.LedgerBoundary.LEDGER_END)
+          .build();
+    } else if (this instanceof Absolute) {
+      Absolute absolute = (Absolute) this;
+      return LedgerOffsetOuterClass.LedgerOffset.newBuilder().setAbsolute(absolute.offset).build();
+    } else {
+      throw new LedgerOffsetUnknown(this);
+    }
+  }
+}
+
+class LedgerBoundaryUnrecognized extends RuntimeException {
+  public LedgerBoundaryUnrecognized(LedgerOffsetOuterClass.LedgerOffset.LedgerBoundary boundary) {
+    super("Ledger Boundary unknown " + boundary.toString());
+  }
+}
+
+class LedgerBoundaryUnset extends RuntimeException {
+  public LedgerBoundaryUnset(LedgerOffsetOuterClass.LedgerOffset offset) {
+    super("Ledger Offset unset " + offset.toString());
+  }
+}
+
+class LedgerOffsetUnknown extends RuntimeException {
+  public LedgerOffsetUnknown(LedgerOffset offset) {
+    super("Ledger offset unkwnown " + offset.toString());
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ListUserRightsRequest.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ListUserRightsRequest.java
new file mode 100644
index 0000000000..a5abccf815
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ListUserRightsRequest.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class ListUserRightsRequest {
+
+  private final String userId;
+
+  public ListUserRightsRequest(@NonNull String userId) {
+    this.userId = userId;
+  }
+
+  public String getId() {
+    return userId;
+  }
+
+  @Override
+  public String toString() {
+    return "ListUserRightsRequest{" + "userId='" + userId + '\'' + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ListUserRightsRequest that = (ListUserRightsRequest) o;
+    return Objects.equals(userId, that.userId);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(userId);
+  }
+
+  public UserManagementServiceOuterClass.ListUserRightsRequest toProto() {
+    return UserManagementServiceOuterClass.ListUserRightsRequest.newBuilder()
+        .setUserId(this.userId)
+        .build();
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ListUserRightsResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ListUserRightsResponse.java
new file mode 100644
index 0000000000..8ca0d01112
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ListUserRightsResponse.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class ListUserRightsResponse {
+
+  private final List rights;
+
+  public ListUserRightsResponse(@NonNull List rights) {
+    this.rights = new ArrayList<>(rights);
+  }
+
+  public List getRights() {
+    return new ArrayList<>(this.rights);
+  }
+
+  public static ListUserRightsResponse fromProto(
+      UserManagementServiceOuterClass.ListUserRightsResponse proto) {
+    return new ListUserRightsResponse(
+        proto.getRightsList().stream().map(User.Right::fromProto).collect(Collectors.toList()));
+  }
+
+  @Override
+  public String toString() {
+    return "ListUserRightsResponse{" + "rights=" + rights + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ListUserRightsResponse that = (ListUserRightsResponse) o;
+    return Objects.equals(rights, that.rights);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(rights);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ListUsersRequest.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ListUsersRequest.java
new file mode 100644
index 0000000000..9a95d796b3
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ListUsersRequest.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+import java.util.Objects;
+import java.util.Optional;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class ListUsersRequest {
+
+  private final Optional pageToken;
+
+  private final Integer pageSize;
+
+  public ListUsersRequest(@NonNull Optional pageToken, @NonNull Integer pageSize) {
+    this.pageToken = pageToken;
+    this.pageSize = pageSize;
+  }
+
+  public Optional getPageToken() {
+    return pageToken;
+  }
+
+  public Integer getPageSize() {
+    return pageSize;
+  }
+
+  @Override
+  public String toString() {
+    return "ListUsersRequest{" + "pageToken=" + pageToken + ", pageSize=" + pageSize + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ListUsersRequest that = (ListUsersRequest) o;
+    return Objects.equals(pageToken, that.pageToken) && Objects.equals(pageSize, that.pageSize);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(pageToken, pageSize);
+  }
+
+  public UserManagementServiceOuterClass.ListUsersRequest toProto() {
+    UserManagementServiceOuterClass.ListUsersRequest.Builder builder =
+        UserManagementServiceOuterClass.ListUsersRequest.newBuilder();
+    pageToken.ifPresent(builder::setPageToken);
+    builder.setPageSize(pageSize);
+    return builder.build();
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ListUsersResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ListUsersResponse.java
new file mode 100644
index 0000000000..dfe9b00097
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/ListUsersResponse.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class ListUsersResponse {
+
+  private final List users;
+
+  public ListUsersResponse(@NonNull List users) {
+    this.users = new ArrayList<>(users);
+  }
+
+  public List getUsers() {
+    return new ArrayList<>(this.users);
+  }
+
+  public static ListUsersResponse fromProto(
+      UserManagementServiceOuterClass.ListUsersResponse proto) {
+    return new ListUsersResponse(
+        proto.getUsersList().stream().map(User::fromProto).collect(Collectors.toList()));
+  }
+
+  @Override
+  public String toString() {
+    return "ListUsersResponse{" + "users=" + users + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ListUsersResponse that = (ListUsersResponse) o;
+    return Objects.equals(users, that.users);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(users);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/NoFilter.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/NoFilter.java
new file mode 100644
index 0000000000..36d8c6dea1
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/NoFilter.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.TransactionFilterOuterClass;
+
+public final class NoFilter extends Filter {
+
+  public static final NoFilter instance = new NoFilter();
+
+  private NoFilter() {}
+
+  @Override
+  public TransactionFilterOuterClass.Filters toProto() {
+    return TransactionFilterOuterClass.Filters.getDefaultInstance();
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Numeric.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Numeric.java
new file mode 100644
index 0000000000..29ad995f06
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Numeric.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.math.BigDecimal;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public class Numeric extends Value {
+
+  private final BigDecimal value;
+
+  public Numeric(@NonNull BigDecimal value) {
+    this.value = value;
+  }
+
+  public static Numeric fromProto(String numeric) {
+    return new Numeric(new BigDecimal(numeric));
+  }
+
+  @Override
+  public ValueOuterClass.Value toProto() {
+    return ValueOuterClass.Value.newBuilder().setNumeric(this.value.toPlainString()).build();
+  }
+
+  @NonNull
+  public BigDecimal getValue() {
+    return value;
+  }
+
+  @Override
+  public String toString() {
+    return "Numeric{" + "value=" + value + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    Numeric numeric = (Numeric) o;
+    return Objects.equals(value, numeric.value);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(value);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Party.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Party.java
new file mode 100644
index 0000000000..3023bc4877
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Party.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.util.Objects;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class Party extends Value {
+
+  private final String value;
+
+  public Party(@NonNull String value) {
+    this.value = value;
+  }
+
+  @Override
+  public ValueOuterClass.Value toProto() {
+    return ValueOuterClass.Value.newBuilder().setParty(this.value).build();
+  }
+
+  @NonNull
+  public String getValue() {
+    return value;
+  }
+
+  @Override
+  public String toString() {
+    return "Party{" + "value='" + value + '\'' + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    Party party = (Party) o;
+    return Objects.equals(value, party.value);
+  }
+
+  @Override
+  public int hashCode() {
+
+    return Objects.hash(value);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Record.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Record.java
new file mode 100644
index 0000000000..c9766bdfcf
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Record.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import static java.util.Collections.unmodifiableList;
+
+import com.daml.ledger.api.v1.ValueOuterClass;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+// FIXME When removing this after the deprecation period is over, make DamlRecord final
+/** @deprecated Use {@link DamlRecord} instead. */
+@Deprecated
+public final class Record extends DamlRecord {
+
+  public Record(@NonNull Identifier recordId, @NonNull Field... fields) {
+    this(recordId, Arrays.asList(fields));
+  }
+
+  public Record(@NonNull Field... fields) {
+    super(Arrays.asList(fields));
+  }
+
+  public Record(@NonNull Identifier recordId, @NonNull List<@NonNull Field> fields) {
+    super(recordId, unmodifiableList(fields));
+  }
+
+  public Record(@NonNull List<@NonNull Field> fields) {
+    super(unmodifiableList(fields));
+  }
+
+  public Record(
+      @NonNull Optional recordId,
+      @NonNull List<@NonNull Field> fields,
+      Map fieldsMap) {
+    super(recordId, unmodifiableList(fields), fieldsMap);
+  }
+
+  /** @deprecated Use {@link DamlRecord#fromProto(ValueOuterClass.Record)} instead */
+  @Deprecated
+  @NonNull
+  public static Record fromProto(ValueOuterClass.Record record) {
+    ArrayList fields = new ArrayList<>(record.getFieldsCount());
+    HashMap fieldsMap = new HashMap<>(record.getFieldsCount());
+    for (ValueOuterClass.RecordField recordField : record.getFieldsList()) {
+      Field field = Field.fromProto(recordField);
+      fields.add(field);
+      if (field.getLabel().isPresent()) {
+        fieldsMap.put(field.getLabel().get(), field.getValue());
+      }
+    }
+    if (record.hasRecordId()) {
+      Identifier recordId = Identifier.fromProto(record.getRecordId());
+      return new Record(Optional.of(recordId), fields, fieldsMap);
+    } else {
+      return new Record(Optional.empty(), fields, fieldsMap);
+    }
+  }
+
+  // FIXME When removing this after the deprecation period is over, make DamlTextMap.Field final
+  /** @deprecated Use {@link DamlRecord.Field} instead. */
+  @Deprecated
+  public static final class Field extends DamlRecord.Field {
+
+    public Field(@NonNull String label, @NonNull Value value) {
+      super(label, value);
+    }
+
+    public Field(@NonNull Value value) {
+      super(value);
+    }
+
+    /** @deprecated Use {@link DamlRecord.Field#fromProto(ValueOuterClass.Record)} instead */
+    @Deprecated
+    public static Field fromProto(ValueOuterClass.RecordField field) {
+      String label = field.getLabel();
+      Value value = Value.fromProto(field.getValue());
+      return label.isEmpty() ? new Field(value) : new Field(label, value);
+    }
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/RevokeUserRightsRequest.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/RevokeUserRightsRequest.java
new file mode 100644
index 0000000000..ac99daac94
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/RevokeUserRightsRequest.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public final class RevokeUserRightsRequest {
+
+  private final String userId;
+  private final List rights;
+
+  public RevokeUserRightsRequest(String userId, User.Right right, User.Right... rights) {
+    this.userId = userId;
+    this.rights = new ArrayList<>(rights.length + 1);
+    this.rights.add(right);
+    this.rights.addAll(Arrays.asList(rights));
+  }
+
+  public String getUserId() {
+    return userId;
+  }
+
+  public List getRights() {
+    return new ArrayList<>(rights);
+  }
+
+  public UserManagementServiceOuterClass.RevokeUserRightsRequest toProto() {
+    return UserManagementServiceOuterClass.RevokeUserRightsRequest.newBuilder()
+        .setUserId(this.userId)
+        .addAllRights(this.rights.stream().map(User.Right::toProto).collect(Collectors.toList()))
+        .build();
+  }
+
+  @Override
+  public String toString() {
+    return "RevokeUserRightsRequest{" + "userId=" + userId + ", rights=" + rights + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    RevokeUserRightsRequest that = (RevokeUserRightsRequest) o;
+    return userId.equals(that.userId) && rights.equals(that.rights);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(userId, rights);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/RevokeUserRightsResponse.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/RevokeUserRightsResponse.java
new file mode 100644
index 0000000000..79fd2d3545
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/RevokeUserRightsResponse.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.admin.UserManagementServiceOuterClass;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class RevokeUserRightsResponse {
+
+  private final List newlyRevokedRights;
+
+  public RevokeUserRightsResponse(@NonNull List newlyRevokedRights) {
+    this.newlyRevokedRights = new ArrayList<>(newlyRevokedRights);
+  }
+
+  public List getNewlyRevokedRights() {
+    return new ArrayList<>(this.newlyRevokedRights);
+  }
+
+  public static RevokeUserRightsResponse fromProto(
+      UserManagementServiceOuterClass.RevokeUserRightsResponse proto) {
+    return new RevokeUserRightsResponse(
+        proto.getNewlyRevokedRightsList().stream()
+            .map(User.Right::fromProto)
+            .collect(Collectors.toList()));
+  }
+
+  @Override
+  public String toString() {
+    return "RevokeUserRightsResponse{" + "newlyRevokedRights=" + newlyRevokedRights + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    RevokeUserRightsResponse that = (RevokeUserRightsResponse) o;
+    return Objects.equals(newlyRevokedRights, that.newlyRevokedRights);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(newlyRevokedRights);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/SubmitAndWaitRequest.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/SubmitAndWaitRequest.java
new file mode 100644
index 0000000000..fb7d970a2f
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/SubmitAndWaitRequest.java
@@ -0,0 +1,146 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.CommandServiceOuterClass;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class SubmitAndWaitRequest {
+
+  public static CommandServiceOuterClass.SubmitAndWaitRequest toProto(
+      @NonNull String ledgerId, @NonNull CommandsSubmission submission) {
+    return CommandServiceOuterClass.SubmitAndWaitRequest.newBuilder()
+        .setCommands(SubmitCommandsRequest.toProto(ledgerId, submission))
+        .build();
+  }
+
+  public static CommandServiceOuterClass.SubmitAndWaitRequest toProto(
+      @NonNull String ledgerId,
+      @NonNull String submissionId,
+      @NonNull CommandsSubmission submission) {
+    return CommandServiceOuterClass.SubmitAndWaitRequest.newBuilder()
+        .setCommands(SubmitCommandsRequest.toProto(ledgerId, submissionId, submission))
+        .build();
+  }
+
+  /** @deprecated since 2.5. Please use {@link #toProto(String, CommandsSubmission)} */
+  @Deprecated
+  public static CommandServiceOuterClass.SubmitAndWaitRequest toProto(
+      @NonNull String ledgerId,
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull String party,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    return CommandServiceOuterClass.SubmitAndWaitRequest.newBuilder()
+        .setCommands(
+            SubmitCommandsRequest.toProto(
+                ledgerId,
+                workflowId,
+                applicationId,
+                commandId,
+                party,
+                minLedgerTimeAbsolute,
+                minLedgerTimeRelative,
+                deduplicationTime,
+                commands))
+        .build();
+  }
+
+  /** @deprecated since 2.5. Please use {@link #toProto(String, String, CommandsSubmission)} */
+  @Deprecated
+  public static CommandServiceOuterClass.SubmitAndWaitRequest toProto(
+      @NonNull String ledgerId,
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull String submissionId,
+      @NonNull String party,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    return CommandServiceOuterClass.SubmitAndWaitRequest.newBuilder()
+        .setCommands(
+            SubmitCommandsRequest.toProto(
+                ledgerId,
+                workflowId,
+                applicationId,
+                commandId,
+                submissionId,
+                party,
+                minLedgerTimeAbsolute,
+                minLedgerTimeRelative,
+                deduplicationTime,
+                commands))
+        .build();
+  }
+
+  /** @deprecated since 2.5. Please use {@link #toProto(String, CommandsSubmission)} */
+  @Deprecated
+  public static CommandServiceOuterClass.SubmitAndWaitRequest toProto(
+      @NonNull String ledgerId,
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull List<@NonNull String> actAs,
+      @NonNull List<@NonNull String> readAs,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    return CommandServiceOuterClass.SubmitAndWaitRequest.newBuilder()
+        .setCommands(
+            SubmitCommandsRequest.toProto(
+                ledgerId,
+                workflowId,
+                applicationId,
+                commandId,
+                actAs,
+                readAs,
+                minLedgerTimeAbsolute,
+                minLedgerTimeRelative,
+                deduplicationTime,
+                commands))
+        .build();
+  }
+
+  /** @deprecated since 2.5. Please use {@link #toProto(String, String, CommandsSubmission)} */
+  @Deprecated
+  public static CommandServiceOuterClass.SubmitAndWaitRequest toProto(
+      @NonNull String ledgerId,
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull String submissionId,
+      @NonNull List<@NonNull String> actAs,
+      @NonNull List<@NonNull String> readAs,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    return CommandServiceOuterClass.SubmitAndWaitRequest.newBuilder()
+        .setCommands(
+            SubmitCommandsRequest.toProto(
+                ledgerId,
+                workflowId,
+                applicationId,
+                commandId,
+                submissionId,
+                actAs,
+                readAs,
+                minLedgerTimeAbsolute,
+                minLedgerTimeRelative,
+                deduplicationTime,
+                commands))
+        .build();
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/SubmitCommandsRequest.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/SubmitCommandsRequest.java
new file mode 100644
index 0000000000..5845dc4abe
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/SubmitCommandsRequest.java
@@ -0,0 +1,571 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import static com.daml.ledger.javaapi.data.codegen.HasCommands.toCommands;
+import static java.util.Arrays.asList;
+
+import com.daml.ledger.api.v1.CommandsOuterClass;
+import com.google.protobuf.Timestamp;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class SubmitCommandsRequest {
+
+  private final String workflowId;
+
+  private final String applicationId;
+
+  private final String commandId;
+
+  private final String party;
+  private final List actAs;
+  private final List readAs;
+
+  private final Optional minLedgerTimeAbsolute;
+  private final Optional minLedgerTimeRelative;
+  private final Optional deduplicationTime;
+  private final Optional submissionId;
+  private final List commands;
+
+  public SubmitCommandsRequest(
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull String party,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    this(
+        workflowId,
+        applicationId,
+        commandId,
+        asList(party),
+        asList(),
+        minLedgerTimeAbsolute,
+        minLedgerTimeRelative,
+        deduplicationTime,
+        commands);
+  }
+
+  public SubmitCommandsRequest(
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull String submissionId,
+      @NonNull String party,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    this(
+        workflowId,
+        applicationId,
+        commandId,
+        asList(party),
+        asList(),
+        minLedgerTimeAbsolute,
+        minLedgerTimeRelative,
+        deduplicationTime,
+        Optional.of(submissionId),
+        commands);
+  }
+
+  public SubmitCommandsRequest(
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull List<@NonNull String> actAs,
+      @NonNull List<@NonNull String> readAs,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    this(
+        workflowId,
+        applicationId,
+        commandId,
+        actAs,
+        readAs,
+        minLedgerTimeAbsolute,
+        minLedgerTimeRelative,
+        deduplicationTime,
+        Optional.empty(),
+        commands);
+  }
+
+  public SubmitCommandsRequest(
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull String submissionId,
+      @NonNull List<@NonNull String> actAs,
+      @NonNull List<@NonNull String> readAs,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    this(
+        workflowId,
+        applicationId,
+        commandId,
+        actAs,
+        readAs,
+        minLedgerTimeAbsolute,
+        minLedgerTimeRelative,
+        deduplicationTime,
+        Optional.of(submissionId),
+        commands);
+  }
+
+  private SubmitCommandsRequest(
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull List<@NonNull String> actAs,
+      @NonNull List<@NonNull String> readAs,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull Optional submissionId,
+      @NonNull List<@NonNull Command> commands) {
+    if (actAs.size() == 0) {
+      throw new IllegalArgumentException("actAs must have at least one element");
+    }
+    this.workflowId = workflowId;
+    this.applicationId = applicationId;
+    this.commandId = commandId;
+    this.party = actAs.get(0);
+    this.actAs = List.copyOf(actAs);
+    this.readAs = List.copyOf(readAs);
+    this.minLedgerTimeAbsolute = minLedgerTimeAbsolute;
+    this.minLedgerTimeRelative = minLedgerTimeRelative;
+    this.deduplicationTime = deduplicationTime;
+    this.submissionId = submissionId;
+    this.commands = commands;
+  }
+
+  public static SubmitCommandsRequest fromProto(CommandsOuterClass.Commands commands) {
+    String workflowId = commands.getWorkflowId();
+    String applicationId = commands.getApplicationId();
+    String commandId = commands.getCommandId();
+    String party = commands.getParty();
+    List actAs = commands.getActAsList();
+    List readAs = commands.getReadAsList();
+    Optional minLedgerTimeAbs =
+        commands.hasMinLedgerTimeAbs()
+            ? Optional.of(
+                Instant.ofEpochSecond(
+                    commands.getMinLedgerTimeAbs().getSeconds(),
+                    commands.getMinLedgerTimeAbs().getNanos()))
+            : Optional.empty();
+    Optional minLedgerTimeRel =
+        commands.hasMinLedgerTimeRel()
+            ? Optional.of(
+                Duration.ofSeconds(
+                    commands.getMinLedgerTimeRel().getSeconds(),
+                    commands.getMinLedgerTimeRel().getNanos()))
+            : Optional.empty();
+    Optional deduplicationPeriod = Optional.empty();
+    switch (commands.getDeduplicationPeriodCase()) {
+      case DEDUPLICATION_DURATION:
+        com.google.protobuf.Duration d = commands.getDeduplicationDuration();
+        deduplicationPeriod = Optional.of(Duration.ofSeconds(d.getSeconds(), d.getNanos()));
+        break;
+      case DEDUPLICATION_TIME:
+        @SuppressWarnings("deprecation")
+        com.google.protobuf.Duration t = commands.getDeduplicationTime();
+        deduplicationPeriod = Optional.of(Duration.ofSeconds(t.getSeconds(), t.getNanos()));
+        break;
+      case DEDUPLICATIONPERIOD_NOT_SET:
+      default:
+        // Backwards compatibility: do not throw, this field could be empty from a previous version
+    }
+    String submissionId = commands.getSubmissionId();
+    ArrayList listOfCommands = new ArrayList<>(commands.getCommandsCount());
+    for (CommandsOuterClass.Command command : commands.getCommandsList()) {
+      listOfCommands.add(Command.fromProtoCommand(command));
+    }
+    if (!actAs.contains(party)) {
+      actAs.add(0, party);
+    }
+    return new SubmitCommandsRequest(
+        workflowId,
+        applicationId,
+        commandId,
+        actAs,
+        readAs,
+        minLedgerTimeAbs,
+        minLedgerTimeRel,
+        deduplicationPeriod,
+        submissionId.isEmpty() ? Optional.empty() : Optional.of(submissionId),
+        listOfCommands);
+  }
+
+  // TODO i15642 Refactor this to take CommmandsSubmission when deprecated methods using it below are
+  // removed
+  private static CommandsOuterClass.Commands deprecatedToProto(
+      @NonNull String ledgerId,
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull List<@NonNull String> actAs,
+      @NonNull List<@NonNull String> readAs,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull Optional submissionId,
+      @NonNull List<@NonNull Command> commands) {
+    if (actAs.size() == 0) {
+      throw new IllegalArgumentException("actAs must have at least one element");
+    }
+    List commandsConverted =
+        commands.stream().map(Command::toProtoCommand).collect(Collectors.toList());
+    CommandsOuterClass.Commands.Builder builder =
+        CommandsOuterClass.Commands.newBuilder()
+            .setLedgerId(ledgerId)
+            .setWorkflowId(workflowId)
+            .setApplicationId(applicationId)
+            .setCommandId(commandId)
+            .setParty(actAs.get(0))
+            .addAllActAs(actAs)
+            .addAllReadAs(readAs)
+            .addAllCommands(commandsConverted);
+    minLedgerTimeAbsolute.ifPresent(
+        abs ->
+            builder.setMinLedgerTimeAbs(
+                Timestamp.newBuilder().setSeconds(abs.getEpochSecond()).setNanos(abs.getNano())));
+    minLedgerTimeRelative.ifPresent(
+        rel ->
+            builder.setMinLedgerTimeRel(
+                com.google.protobuf.Duration.newBuilder()
+                    .setSeconds(rel.getSeconds())
+                    .setNanos(rel.getNano())));
+    deduplicationTime.ifPresent(
+        dedup -> {
+          @SuppressWarnings("deprecation")
+          var unused =
+              builder.setDeduplicationTime(
+                  com.google.protobuf.Duration.newBuilder()
+                      .setSeconds(dedup.getSeconds())
+                      .setNanos(dedup.getNano()));
+        });
+    submissionId.ifPresent(builder::setSubmissionId);
+    return builder.build();
+  }
+
+  private static CommandsOuterClass.Commands toProto(
+      @NonNull String ledgerId,
+      @NonNull Optional submissionId,
+      @NonNull CommandsSubmission submission) {
+
+    if (submission.getActAs().size() == 0) {
+      throw new IllegalArgumentException("actAs must have at least one element");
+    }
+
+    List commands = toCommands(submission.getCommands());
+    List commandsConverted =
+        commands.stream().map(Command::toProtoCommand).collect(Collectors.toList());
+    List disclosedContracts =
+        submission.getDisclosedContracts().stream()
+            .map(DisclosedContract::toProto)
+            .collect(Collectors.toList());
+
+    CommandsOuterClass.Commands.Builder builder =
+        CommandsOuterClass.Commands.newBuilder()
+            .setLedgerId(ledgerId)
+            .setApplicationId(submission.getApplicationId())
+            .setCommandId(submission.getCommandId())
+            .setParty(submission.getActAs().get(0))
+            .addAllActAs(submission.getActAs())
+            .addAllReadAs(submission.getReadAs())
+            .addAllCommands(commandsConverted)
+            .addAllDisclosedContracts(disclosedContracts);
+
+    submission
+        .getMinLedgerTimeAbs()
+        .ifPresent(
+            abs ->
+                builder.setMinLedgerTimeAbs(
+                    Timestamp.newBuilder()
+                        .setSeconds(abs.getEpochSecond())
+                        .setNanos(abs.getNano())));
+
+    submission
+        .getMinLedgerTimeRel()
+        .ifPresent(
+            rel ->
+                builder.setMinLedgerTimeRel(
+                    com.google.protobuf.Duration.newBuilder()
+                        .setSeconds(rel.getSeconds())
+                        .setNanos(rel.getNano())));
+
+    submission
+        .getDeduplicationTime()
+        .ifPresent(
+            dedup -> {
+              @SuppressWarnings("deprecation")
+              var unused =
+                  builder.setDeduplicationTime(
+                      com.google.protobuf.Duration.newBuilder()
+                          .setSeconds(dedup.getSeconds())
+                          .setNanos(dedup.getNano()));
+            });
+
+    submission.getWorkflowId().ifPresent(builder::setWorkflowId);
+    submissionId.ifPresent(builder::setSubmissionId);
+
+    return builder.build();
+  }
+
+  public static CommandsOuterClass.Commands toProto(
+      @NonNull String ledgerId, @NonNull CommandsSubmission submission) {
+    return toProto(ledgerId, Optional.empty(), submission);
+  }
+
+  /** @deprecated since 2.5. Please use {@link #toProto(String, CommandsSubmission)} */
+  @Deprecated
+  public static CommandsOuterClass.Commands toProto(
+      @NonNull String ledgerId,
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull List<@NonNull String> actAs,
+      @NonNull List<@NonNull String> readAs,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    return deprecatedToProto(
+        ledgerId,
+        workflowId,
+        applicationId,
+        commandId,
+        actAs,
+        readAs,
+        minLedgerTimeAbsolute,
+        minLedgerTimeRelative,
+        deduplicationTime,
+        Optional.empty(),
+        commands);
+  }
+
+  public static CommandsOuterClass.Commands toProto(
+      @NonNull String ledgerId,
+      @NonNull String submissionId,
+      @NonNull CommandsSubmission submission) {
+    return toProto(ledgerId, Optional.of(submissionId), submission);
+  }
+
+  /** @deprecated since 2.5. Please use {@link #toProto(String, String, CommandsSubmission)} */
+  @Deprecated
+  public static CommandsOuterClass.Commands toProto(
+      @NonNull String ledgerId,
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull String submissionId,
+      @NonNull List<@NonNull String> actAs,
+      @NonNull List<@NonNull String> readAs,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    return deprecatedToProto(
+        ledgerId,
+        workflowId,
+        applicationId,
+        commandId,
+        actAs,
+        readAs,
+        minLedgerTimeAbsolute,
+        minLedgerTimeRelative,
+        deduplicationTime,
+        Optional.of(submissionId),
+        commands);
+  }
+
+  /** @deprecated since 2.5. Please use {@link #toProto(String, String, CommandsSubmission)} */
+  @Deprecated
+  public static CommandsOuterClass.Commands toProto(
+      @NonNull String ledgerId,
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull String submissionId,
+      @NonNull String party,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    List empty_read_as = new ArrayList<>();
+    List act_as = new ArrayList<>();
+    act_as.add(party);
+    return deprecatedToProto(
+        ledgerId,
+        workflowId,
+        applicationId,
+        commandId,
+        act_as,
+        empty_read_as,
+        minLedgerTimeAbsolute,
+        minLedgerTimeRelative,
+        deduplicationTime,
+        Optional.of(submissionId),
+        commands);
+  }
+
+  /** @deprecated since 2.5. Please use {@link #toProto(String, CommandsSubmission)} */
+  @Deprecated
+  public static CommandsOuterClass.Commands toProto(
+      @NonNull String ledgerId,
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull String party,
+      @NonNull Optional minLedgerTimeAbsolute,
+      @NonNull Optional minLedgerTimeRelative,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    List empty_read_as = new ArrayList<>();
+    List act_as = new ArrayList<>();
+    act_as.add(party);
+    return deprecatedToProto(
+        ledgerId,
+        workflowId,
+        applicationId,
+        commandId,
+        act_as,
+        empty_read_as,
+        minLedgerTimeAbsolute,
+        minLedgerTimeRelative,
+        deduplicationTime,
+        Optional.empty(),
+        commands);
+  }
+
+  @NonNull
+  public String getWorkflowId() {
+    return workflowId;
+  }
+
+  @NonNull
+  public String getApplicationId() {
+    return applicationId;
+  }
+
+  @NonNull
+  public String getCommandId() {
+    return commandId;
+  }
+
+  @NonNull
+  public String getParty() {
+    return party;
+  }
+
+  @NonNull
+  public List getActAs() {
+    return actAs;
+  }
+
+  @NonNull
+  public List getReadAs() {
+    return readAs;
+  }
+
+  @NonNull
+  public Optional getMinLedgerTimeAbsolute() {
+    return minLedgerTimeAbsolute;
+  }
+
+  @NonNull
+  public Optional getMinLedgerTimeRelative() {
+    return minLedgerTimeRelative;
+  }
+
+  @NonNull
+  public Optional getDeduplicationTime() {
+    return deduplicationTime;
+  }
+
+  @NonNull
+  public Optional getSubmissionId() {
+    return submissionId;
+  }
+
+  @NonNull
+  public List<@NonNull Command> getCommands() {
+    return commands;
+  }
+
+  @Override
+  public String toString() {
+    return "SubmitCommandsRequest{"
+        + "workflowId='"
+        + workflowId
+        + '\''
+        + ", applicationId='"
+        + applicationId
+        + '\''
+        + ", commandId='"
+        + commandId
+        + '\''
+        + ", party='"
+        + party
+        + '\''
+        + ", minLedgerTimeAbs="
+        + minLedgerTimeAbsolute
+        + ", minLedgerTimeRel="
+        + minLedgerTimeRelative
+        + ", deduplicationTime="
+        + deduplicationTime
+        + ", submissionId="
+        + submissionId
+        + ", commands="
+        + commands
+        + '}';
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    SubmitCommandsRequest submitCommandsRequest1 = (SubmitCommandsRequest) o;
+    return Objects.equals(workflowId, submitCommandsRequest1.workflowId)
+        && Objects.equals(applicationId, submitCommandsRequest1.applicationId)
+        && Objects.equals(commandId, submitCommandsRequest1.commandId)
+        && Objects.equals(party, submitCommandsRequest1.party)
+        && Objects.equals(actAs, submitCommandsRequest1.actAs)
+        && Objects.equals(readAs, submitCommandsRequest1.readAs)
+        && Objects.equals(minLedgerTimeAbsolute, submitCommandsRequest1.minLedgerTimeAbsolute)
+        && Objects.equals(minLedgerTimeRelative, submitCommandsRequest1.minLedgerTimeRelative)
+        && Objects.equals(deduplicationTime, submitCommandsRequest1.deduplicationTime)
+        && Objects.equals(submissionId, submitCommandsRequest1.submissionId)
+        && Objects.equals(commands, submitCommandsRequest1.commands);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(
+        workflowId,
+        applicationId,
+        commandId,
+        party,
+        actAs,
+        readAs,
+        minLedgerTimeAbsolute,
+        minLedgerTimeRelative,
+        deduplicationTime,
+        submissionId,
+        commands);
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/SubmitRequest.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/SubmitRequest.java
new file mode 100644
index 0000000000..956779ed97
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/SubmitRequest.java
@@ -0,0 +1,146 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.api.v1.CommandSubmissionServiceOuterClass;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public final class SubmitRequest {
+
+  public static CommandSubmissionServiceOuterClass.SubmitRequest toProto(
+      @NonNull String ledgerId, @NonNull CommandsSubmission submission) {
+    return CommandSubmissionServiceOuterClass.SubmitRequest.newBuilder()
+        .setCommands(SubmitCommandsRequest.toProto(ledgerId, submission))
+        .build();
+  }
+
+  public static CommandSubmissionServiceOuterClass.SubmitRequest toProto(
+      @NonNull String ledgerId,
+      @NonNull String submissionId,
+      @NonNull CommandsSubmission submission) {
+    return CommandSubmissionServiceOuterClass.SubmitRequest.newBuilder()
+        .setCommands(SubmitCommandsRequest.toProto(ledgerId, submissionId, submission))
+        .build();
+  }
+
+  /** @deprecated since 2.5. Please use {@link #toProto(String, CommandsSubmission)} */
+  @Deprecated
+  public static CommandSubmissionServiceOuterClass.SubmitRequest toProto(
+      @NonNull String ledgerId,
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull String party,
+      @NonNull Optional minLedgerTimeAbs,
+      @NonNull Optional minLedgerTimeRel,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    return CommandSubmissionServiceOuterClass.SubmitRequest.newBuilder()
+        .setCommands(
+            SubmitCommandsRequest.toProto(
+                ledgerId,
+                workflowId,
+                applicationId,
+                commandId,
+                party,
+                minLedgerTimeAbs,
+                minLedgerTimeRel,
+                deduplicationTime,
+                commands))
+        .build();
+  }
+
+  /** @deprecated since 2.5. Please use {@link #toProto(String, CommandsSubmission)} */
+  @Deprecated
+  public static CommandSubmissionServiceOuterClass.SubmitRequest toProto(
+      @NonNull String ledgerId,
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull List<@NonNull String> actAs,
+      @NonNull List<@NonNull String> readAs,
+      @NonNull Optional minLedgerTimeAbs,
+      @NonNull Optional minLedgerTimeRel,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    return CommandSubmissionServiceOuterClass.SubmitRequest.newBuilder()
+        .setCommands(
+            SubmitCommandsRequest.toProto(
+                ledgerId,
+                workflowId,
+                applicationId,
+                commandId,
+                actAs,
+                readAs,
+                minLedgerTimeAbs,
+                minLedgerTimeRel,
+                deduplicationTime,
+                commands))
+        .build();
+  }
+
+  /** @deprecated since 2.5. Please use {@link #toProto(String, String, CommandsSubmission)} */
+  @Deprecated
+  public static CommandSubmissionServiceOuterClass.SubmitRequest toProto(
+      @NonNull String ledgerId,
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull String submissionId,
+      @NonNull String party,
+      @NonNull Optional minLedgerTimeAbs,
+      @NonNull Optional minLedgerTimeRel,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    return CommandSubmissionServiceOuterClass.SubmitRequest.newBuilder()
+        .setCommands(
+            SubmitCommandsRequest.toProto(
+                ledgerId,
+                workflowId,
+                applicationId,
+                commandId,
+                submissionId,
+                party,
+                minLedgerTimeAbs,
+                minLedgerTimeRel,
+                deduplicationTime,
+                commands))
+        .build();
+  }
+
+  /** @deprecated since 2.5. Please use {@link #toProto(String, String, CommandsSubmission)} */
+  @Deprecated
+  public static CommandSubmissionServiceOuterClass.SubmitRequest toProto(
+      @NonNull String ledgerId,
+      @NonNull String workflowId,
+      @NonNull String applicationId,
+      @NonNull String commandId,
+      @NonNull String submissionId,
+      @NonNull List<@NonNull String> actAs,
+      @NonNull List<@NonNull String> readAs,
+      @NonNull Optional minLedgerTimeAbs,
+      @NonNull Optional minLedgerTimeRel,
+      @NonNull Optional deduplicationTime,
+      @NonNull List<@NonNull Command> commands) {
+    return CommandSubmissionServiceOuterClass.SubmitRequest.newBuilder()
+        .setCommands(
+            SubmitCommandsRequest.toProto(
+                ledgerId,
+                workflowId,
+                applicationId,
+                commandId,
+                submissionId,
+                actAs,
+                readAs,
+                minLedgerTimeAbs,
+                minLedgerTimeRel,
+                deduplicationTime,
+                commands))
+        .build();
+  }
+}
diff --git a/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Template.java b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Template.java
new file mode 100644
index 0000000000..00fed1adcb
--- /dev/null
+++ b/canton/community/bindings-java/src/main/java/com/daml/ledger/javaapi/data/Template.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates.
+// Proprietary code. All rights reserved.
+
+package com.daml.ledger.javaapi.data;
+
+import com.daml.ledger.javaapi.data.codegen.Contract;
+import com.daml.ledger.javaapi.data.codegen.ContractCompanion;
+import com.daml.ledger.javaapi.data.codegen.ContractId;
+import com.daml.ledger.javaapi.data.codegen.CreateAnd;
+import com.daml.ledger.javaapi.data.codegen.Created;
+import com.daml.ledger.javaapi.data.codegen.Update;
+
+public abstract class Template extends com.daml.ledger.javaapi.data.codegen.DamlRecord