- Overview
- Tutorials
- Getting started
- Get started with Canton and the JSON Ledger API
- Get Started with Canton, the JSON Ledger API, and TypeScript
- Get started with Canton Network App Dev Quickstart
- Get started with smart contract development
- Basic contracts
- Test templates using Daml scripts
- Build the Daml Archive (.dar) file
- Data types
- Transform contracts using choices
- Add constraints to a contract
- Parties and authority
- Compose choices
- Handle exceptions
- Work with dependencies
- Functional programming 101
- The Daml standard library
- Test Daml contracts
- Next steps
- Application development
- Getting started
- Development how-tos
- Component how-tos
- Explanations
- References
- Application development
- Smart contract development
- Daml language cheat sheet
- Daml language reference
- Daml standard library
- DA.Action.State.Class
- DA.Action.State
- DA.Action
- DA.Assert
- DA.Bifunctor
- DA.Crypto.Text
- DA.Date
- DA.Either
- DA.Exception
- DA.Fail
- DA.Foldable
- DA.Functor
- DA.Internal.Interface.AnyView.Types
- DA.Internal.Interface.AnyView
- DA.List.BuiltinOrder
- DA.List.Total
- DA.List
- DA.Logic
- DA.Map
- DA.Math
- DA.Monoid
- DA.NonEmpty.Types
- DA.NonEmpty
- DA.Numeric
- DA.Optional
- DA.Record
- DA.Semigroup
- DA.Set
- DA.Stack
- DA.Text
- DA.TextMap
- DA.Time
- DA.Traversable
- DA.Tuple
- DA.Validation
- GHC.Show.Text
- GHC.Tuple.Check
- Prelude
- Smart contract upgrading reference
- Glossary of concepts
Get Started with Canton, the JSON Ledger API, and TypeScript¶
This tutorial shows you how to interact with a Canton Ledger using the JSON Ledger API and TypeScript.
Overview¶
You will use and modify an existing example project provided with the Canton distribution.
Prerequisites¶
You should be familiar with the basics of TypeScript and tools such as npm and node.js.
Tools¶
Before starting, ensure you have the following installed:
Node.js and npm — Download from https://nodejs.org/en/download/. Recommended version: 18.20.x or later.
Daml SDK — Install using:
curl -sSL https://get.daml.com/ | sh -s
Verify the installation by running:
daml version
You should see a version equal to or later than .
Canton — Includes pre-built examples. See the Canton demo for details.
Example TypeScript Project¶
Open a terminal and navigate to the JSON Ledger API example folder:
cd <canton_installation>/examples/09-json-ledger-api
Start Canton:
./run.sh
Once the Canton console is ready, open a new terminal and navigate to the TypeScript example folder:
cd <canton_installation>/examples/09-json-ledger-api/typescript
Running the Example¶
Install the project dependencies:
npm install
You may see some warnings, which can be ignored for now.
The JSON Ledger API provides an OpenAPI specification. You can use this to generate TypeScript client classes (stubs).
To generate the TypeScript client:
npm run generate_api
Check the generated code in the generated/api folder. It should include the TypeScript classes for the API.
less generated/api/ledger-api.d.ts
This file contains definitions for services and models, such as /v2/commands/submit-and-wait, CreateCommand, and many others.
Next, generate TypeScript types from the Daml model:
npm run generate_daml_bindings
This creates bindings in the ./generated folder. Each Daml module has its own subfolder, for example: generated/model-tests-1.0.0/lib/Iou.
Now compile the TypeScript code:
npm run build
Once the build succeeds, run the example:
npm run scenario
You should see output similar to:
Alice creates contract
Ledger offset: 23
...
Bob accepts transfer
...
End of scenario
Code Highlights¶
The JSON Ledger API client is configured in src/client.ts:
import createClient from "openapi-fetch";
import type { paths } from "../generated/api/ledger-api";
export const client = createClient<paths>({ baseUrl: "http://localhost:7575" });
The openapi-fetch library is used to create the API client.
Allocating a Party¶
In src/user.ts, a party is allocated as follows:
const resp = await client.POST("/v2/parties", {
body: {
partyIdHint: user,
identityProviderId: "",
}
});
Note
openapi-fetch uses the Indexed Access Types TypeScript feature to provide type safety. The example above looks like untyped JavaScript, but it is in fact type-safe.
Creating a Contract¶
In src/commands.ts, a contract is created using:
export async function createContract(userParty: string): Promise<components["schemas"]["SubmitAndWaitResponse"]> {
const iou: Iou.Iou = {
issuer: userParty,
owner: userParty,
currency: "USD",
amount: "100",
observers: []
};
const command: components["schemas"]["CreateCommand"] = {
createArguments: iou,
templateId: Iou.Iou.templateId
};
const jsCommands = makeCommands(userParty, [{ CreateCommand: command }]);
const resp = await client.POST("/v2/commands/submit-and-wait", {
body: jsCommands
});
return valueOrError(resp);
}
Querying Contracts¶
In src/index.ts, contracts are queried like this:
const { data, error } = await client.POST("/v2/state/active-contracts", {
body: filter
});
if (data === undefined)
return Promise.reject(error);
else {
const contracts: components["schemas"]["CreatedEvent"][] = data
.map((res) => res.contractEntry)
.filter((res) => "JsActiveContract" in res)
.map((res) => res.JsActiveContract.createdEvent);
return Promise.resolve(contracts);
}
Extending the Example¶
In this step, you extend the Iou template with a new field and update the TypeScript code accordingly.
Open the Io.daml file in canton/examples/09-json-ledger-api/model.
Modify the Iou template to include a new comment field:
template Iou
with
issuer : Party
owner : Party
currency : Text
amount : Decimal
observers : [Party]
comment : Optional Text -- added field
where
-- leave the rest of the template unchanged
> The comment field is optional, making this a backwards-compatible change.
Stop the Canton server (Ctrl+C) and restart it:
./run.sh
Rebuild the TypeScript bindings:
npm run generate_daml_bindings
Update the createContract function in src/commands.ts to include the new field:
export async function createContract(userParty: string): Promise<components["schemas"]["SubmitAndWaitResponse"]> {
const iou: Iou.Iou = {
issuer: userParty,
owner: userParty,
currency: "USD",
amount: "100",
observers: [],
comment: "This is a test comment" // new field
};
// leave the rest of the function unchanged
Rebuild the TypeScript code:
npm run build
Run the example:
npm run scenario
You won’t see the new comment field in the output yet. To display it, modify the showAcs function call in src/index.ts to include the new field:
showAcs(contracts, ["owner", "amount", "currency", "comment"], c => [c.owner, c.amount, c.currency, c.comment]);
The, execute the npm run scenario once again.