create-canton-app 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # create-canton-app
2
+ Scaffold Canton Network Daml projects in seconds. A CLI tool to quickly bootstrap Canton Network dApp.
3
+
4
+ ## Adding Custom Templates
5
+
6
+ 1. Create Your Template folder with a 'daml' subdir (rename 'my-template' as your desired template name)
7
+
8
+ ```bash
9
+ mkdir -p src/templates/my-template/daml
10
+ ```
11
+
12
+ 2. Add your Daml contract
13
+
14
+ Create `src/templates/my-template/daml/MyContract.daml`
15
+
16
+ 3. Update CLI choices
17
+
18
+ Edit `src/commands/create.js` line 43:
19
+
20
+ ```javascript
21
+ choices: [
22
+ { name: 'Token Contract', value: 'token' },
23
+
24
+ { name: 'Escrow Contract', value: 'escrow' },
25
+
26
+ { name: 'My Template', value: 'my-template' }, // ADD THIS
27
+
28
+ { name: '📄 Empty Template', value: 'empty' }
29
+ ]
30
+ ```
31
+
32
+ 4. Test it
33
+
34
+ ```bash
35
+ cd test-my-template
36
+ daml build
37
+ daml test
38
+ ```
39
+
40
+ ## Template Checklist
41
+
42
+ - [ ] Compiles with `daml build`
43
+ - [ ] Tests pass with `daml test`
44
+ - [ ] Includes inline comments
45
+ - [ ] Has clear use case
46
+ - [ ] daml.yaml includes `daml-script` dependency
47
+
48
+ ## Requirements
49
+ - Node.js v16+
50
+ - Daml SDK v3.4.9+
51
+ - Java Runtime (for tests)
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const create = require('../src/commands/create');
5
+ const compile = require('../src/commands/compile');
6
+ const test = require('../src/commands/test');
7
+
8
+ // Simple banner without chalk.bold
9
+ console.log('\x1b[36m%s\x1b[0m', `
10
+ ____ _
11
+ / ___|__ _ _ __ | |_ ___ _ __
12
+ | | / _\` | '_ \\| __/ _ \\| '_ \\
13
+ | |__| (_| | | | | || (_) | | | |
14
+ \\____\\__,_|_| |_|\\__\\___/|_| |_|
15
+
16
+ Canton Network Developer CLI
17
+ Build privacy-first dApps in minutes
18
+ `);
19
+
20
+ program
21
+ .name('create-canton-app')
22
+ .description('Scaffold Canton/Daml projects instantly')
23
+ .version('0.1.0');
24
+
25
+ // Main create command
26
+ program
27
+ .argument('[project-name]', 'Name of your project')
28
+ .option('-t, --template <type>', 'Template to use (token, escrow, empty)', 'token')
29
+ .option('--no-tests', 'Skip test files')
30
+ .action(create);
31
+
32
+ // Compile command
33
+ program
34
+ .command('compile')
35
+ .description('Compile your Daml contracts')
36
+ .action(compile);
37
+
38
+ // Test command
39
+ program
40
+ .command('test')
41
+ .description('Run your contract tests')
42
+ .action(test);
43
+
44
+ program.parse(process.argv);
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "create-canton-app",
3
+ "version": "1.0.0",
4
+ "description": "Scaffold Canton Network/Daml projects in seconds",
5
+ "main": "bin/create-canton-app.js",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Jatinp26/create-canton-app.git"
9
+ },
10
+ "bin": {
11
+ "create-canton-app": "./bin/create-canton-app.js",
12
+ "canton-dev": "./bin/create-canton-app.js"
13
+ },
14
+ "scripts": {
15
+ "test": "echo \"Error: no test specified\" && exit 1"
16
+ },
17
+ "keywords": [
18
+ "canton",
19
+ "daml",
20
+ "blockchain"
21
+ ],
22
+ "author": "Jatin Pandya <Jatin@canton.foundation>",
23
+ "license": "MIT",
24
+ "dependencies": {
25
+ "chalk": "4.1.2",
26
+ "commander": "^11.1.0",
27
+ "fs-extra": "^11.2.0",
28
+ "inquirer": "8.2.6",
29
+ "ora": "5.4.1",
30
+ "shelljs": "^0.8.5"
31
+ }
32
+ }
@@ -0,0 +1,39 @@
1
+ const ora = require('ora');
2
+ const shell = require('shelljs');
3
+
4
+ const colors = {
5
+ red: (text) => `\x1b[31m${text}\x1b[0m`,
6
+ green: (text) => `\x1b[32m${text}\x1b[0m`,
7
+ yellow: (text) => `\x1b[33m${text}\x1b[0m`,
8
+ white: (text) => `\x1b[37m${text}\x1b[0m`
9
+ };
10
+
11
+ function compile() {
12
+ console.log('');
13
+ const spinner = ora('Compiling Daml contracts...').start();
14
+
15
+ // Check if daml is installed
16
+ if (!shell.which('daml')) {
17
+ spinner.fail(colors.red('Daml SDK not found!'));
18
+ console.log('');
19
+ console.log(colors.yellow('Install Daml SDK:'));
20
+ console.log(colors.white(' curl -sSL https://get.daml.com/ | sh'));
21
+ console.log('');
22
+ process.exit(1);
23
+ }
24
+
25
+ // Run daml build
26
+ const result = shell.exec('daml build', { silent: true });
27
+
28
+ if (result.code !== 0) {
29
+ spinner.fail(colors.red('Compilation failed!'));
30
+ console.log('');
31
+ console.log(colors.red(result.stderr));
32
+ process.exit(1);
33
+ }
34
+
35
+ spinner.succeed(colors.green('Contracts compiled successfully!'));
36
+ console.log('');
37
+ }
38
+
39
+ module.exports = compile;
@@ -0,0 +1,163 @@
1
+ const inquirer = require('inquirer');
2
+ const ora = require('ora');
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+ const shell = require('shelljs');
6
+
7
+ const colors = {
8
+ red: (text) => `\x1b[31m${text}\x1b[0m`,
9
+ green: (text) => `\x1b[32m${text}\x1b[0m`,
10
+ cyan: (text) => `\x1b[36m${text}\x1b[0m`,
11
+ yellow: (text) => `\x1b[33m${text}\x1b[0m`,
12
+ white: (text) => `\x1b[37m${text}\x1b[0m`,
13
+ dim: (text) => `\x1b[2m${text}\x1b[0m`,
14
+ bold: (text) => `\x1b[1m${text}\x1b[0m`
15
+ };
16
+
17
+ async function create(projectName, options) {
18
+ try {
19
+ if (!projectName) {
20
+ const answers = await inquirer.prompt([
21
+ {
22
+ type: 'input',
23
+ name: 'projectName',
24
+ message: 'What is your project name?',
25
+ default: 'my-canton-app'
26
+ }
27
+ ]);
28
+ projectName = answers.projectName;
29
+ }
30
+
31
+ let template = options.template;
32
+ if (!template) {
33
+ const answers = await inquirer.prompt([
34
+ {
35
+ type: 'list',
36
+ name: 'template',
37
+ message: 'Which template would you like to use?',
38
+ choices: [
39
+ { name: '🪙 Token Contract (fungible token like ERC20)', value: 'token' },
40
+ { name: '🤝 Escrow Contract (multi-party escrow)', value: 'escrow' },
41
+ { name: '📄 Empty Template (blank starter)', value: 'empty' }
42
+ ]
43
+ }
44
+ ]);
45
+ template = answers.template;
46
+ }
47
+
48
+ console.log('');
49
+ const spinner = ora('Creating your Canton project...').start();
50
+
51
+ const projectPath = path.join(process.cwd(), projectName);
52
+
53
+ if (fs.existsSync(projectPath)) {
54
+ spinner.fail(colors.red(`Folder ${projectName} already exists!`));
55
+ process.exit(1);
56
+ }
57
+
58
+ fs.mkdirSync(projectPath);
59
+
60
+ const templatePath = path.join(__dirname, '../templates', template);
61
+ if (fs.existsSync(templatePath)) {
62
+ fs.copySync(templatePath, projectPath);
63
+ } else {
64
+ spinner.warn(colors.yellow(`Template ${template} not found, creating empty structure...`));
65
+ fs.mkdirSync(path.join(projectPath, 'daml'), { recursive: true });
66
+ }
67
+
68
+ fs.mkdirSync(path.join(projectPath, 'scripts'), { recursive: true });
69
+ fs.mkdirSync(path.join(projectPath, 'config'), { recursive: true });
70
+
71
+ createScripts(projectPath);
72
+ createConfig(projectPath, projectName);
73
+ createReadme(projectPath, projectName, template);
74
+ createGitignore(projectPath);
75
+
76
+ spinner.succeed(colors.green('✨ Project created successfully!'));
77
+
78
+ console.log('');
79
+ console.log(colors.cyan(colors.bold('📚 Next steps:')));
80
+ console.log('');
81
+ console.log(colors.white(` cd ${projectName}`));
82
+ console.log(colors.white(` daml build ${colors.dim('# Compile your contracts')}`));
83
+ console.log(colors.white(` daml test ${colors.dim('# Run tests')}`));
84
+ console.log('');
85
+ console.log(colors.dim('📖 Read README.md for more information'));
86
+ console.log('');
87
+
88
+ } catch (error) {
89
+ console.error(colors.red('❌ Error creating project:'), error.message);
90
+ process.exit(1);
91
+ }
92
+ }
93
+
94
+ function createScripts(projectPath) {
95
+ const compileScript = `#!/bin/bash
96
+ echo "🔨 Compiling Daml contracts..."
97
+ daml build
98
+ `;
99
+ fs.writeFileSync(path.join(projectPath, 'scripts/compile.sh'), compileScript);
100
+
101
+ const testScript = `#!/bin/bash
102
+ echo "🧪 Running tests..."
103
+ daml test
104
+ `;
105
+ fs.writeFileSync(path.join(projectPath, 'scripts/test.sh'), testScript);
106
+
107
+ try {
108
+ shell.chmod('+x', path.join(projectPath, 'scripts/*.sh'));
109
+ } catch (e) {}
110
+ }
111
+
112
+ function createConfig(projectPath, projectName) {
113
+ // THE FIX: Added daml-script dependency!
114
+ const damlConfig = `sdk-version: 3.4.9
115
+ name: ${projectName}
116
+ source: daml
117
+ version: 1.0.0
118
+ dependencies:
119
+ - daml-prim
120
+ - daml-stdlib
121
+ - daml-script
122
+ `;
123
+ fs.writeFileSync(path.join(projectPath, 'daml.yaml'), damlConfig);
124
+ }
125
+
126
+ function createGitignore(projectPath) {
127
+ const gitignore = `.daml/
128
+ *.dar
129
+ *.log
130
+ node_modules/
131
+ .DS_Store
132
+ `;
133
+ fs.writeFileSync(path.join(projectPath, '.gitignore'), gitignore);
134
+ }
135
+
136
+ function createReadme(projectPath, projectName, template) {
137
+ const readme = `# ${projectName}
138
+
139
+ > A Canton Network dApp built with create-canton-app
140
+
141
+ ## Template: ${template}
142
+
143
+ ## Quick Start
144
+
145
+ \`\`\`bash
146
+ # Compile contracts
147
+ daml build
148
+
149
+ # Run tests
150
+ daml test
151
+ \`\`\`
152
+
153
+ ## Learn More
154
+
155
+ - [Canton Docs](https://docs.digitalasset.com)
156
+ - [Daml Docs](https://docs.daml.com)
157
+ - [Canton Network](https://canton.network)
158
+ `;
159
+
160
+ fs.writeFileSync(path.join(projectPath, 'README.md'), readme);
161
+ }
162
+
163
+ module.exports = create;
@@ -0,0 +1,41 @@
1
+ const ora = require('ora');
2
+ const shell = require('shelljs');
3
+
4
+ const colors = {
5
+ red: (text) => `\x1b[31m${text}\x1b[0m`,
6
+ green: (text) => `\x1b[32m${text}\x1b[0m`,
7
+ yellow: (text) => `\x1b[33m${text}\x1b[0m`,
8
+ white: (text) => `\x1b[37m${text}\x1b[0m`,
9
+ dim: (text) => `\x1b[2m${text}\x1b[0m`
10
+ };
11
+
12
+ function test() {
13
+ console.log('');
14
+ const spinner = ora('Running tests...').start();
15
+
16
+ // Check if daml is installed
17
+ if (!shell.which('daml')) {
18
+ spinner.fail(colors.red('Daml SDK not found!'));
19
+ console.log('');
20
+ console.log(colors.yellow('Install Daml SDK:'));
21
+ console.log(colors.white(' curl -sSL https://get.daml.com/ | sh'));
22
+ console.log('');
23
+ process.exit(1);
24
+ }
25
+
26
+ // Run daml test
27
+ const result = shell.exec('daml test', { silent: true });
28
+
29
+ if (result.code !== 0) {
30
+ spinner.fail(colors.red('Tests failed!'));
31
+ console.log('');
32
+ console.log(colors.red(result.stderr));
33
+ process.exit(1);
34
+ }
35
+
36
+ spinner.succeed(colors.green('All tests passed!'));
37
+ console.log('');
38
+ console.log(colors.dim(result.stdout));
39
+ }
40
+
41
+ module.exports = test;
@@ -0,0 +1,22 @@
1
+ -- Main.daml
2
+ -- Your Canton smart contract starts here!
3
+
4
+ module Main where
5
+
6
+ import Daml.Script
7
+
8
+ template MyContract
9
+ with
10
+ party1 : Party
11
+ where
12
+ signatory party1
13
+
14
+ setup : Script ()
15
+ setup = script do
16
+ alice <- allocateParty "Alice"
17
+
18
+ submit alice do
19
+ createCmd MyContract with
20
+ party1 = alice
21
+
22
+ return ()
@@ -0,0 +1,53 @@
1
+ -- Escrow.daml
2
+ -- A two-party escrow with an arbiter
3
+
4
+ module Escrow where
5
+
6
+ import Daml.Script
7
+
8
+ template EscrowAgreement
9
+ with
10
+ buyer : Party
11
+ seller : Party
12
+ arbiter : Party
13
+ amount : Decimal
14
+ description : Text
15
+ where
16
+ signatory buyer, seller, arbiter
17
+
18
+ choice Release : ()
19
+ controller buyer
20
+ do
21
+ return ()
22
+
23
+ choice Dispute : ()
24
+ with
25
+ decision : Text
26
+ controller arbiter
27
+ do
28
+ return ()
29
+
30
+ choice Cancel : ()
31
+ controller buyer, seller
32
+ do
33
+ return ()
34
+
35
+ setup : Script ()
36
+ setup = script do
37
+ alice <- allocateParty "Alice"
38
+ bob <- allocateParty "Bob"
39
+ charlie <- allocateParty "Charlie"
40
+
41
+ escrow <- submit alice do
42
+ submitMulti [alice, bob, charlie] [] do
43
+ createCmd EscrowAgreement with
44
+ buyer = alice
45
+ seller = bob
46
+ arbiter = charlie
47
+ amount = 1000.0
48
+ description = "Laptop purchase"
49
+
50
+ submit alice do
51
+ exerciseCmd escrow Release
52
+
53
+ return ()
@@ -0,0 +1,67 @@
1
+ -- Tests.daml
2
+ -- Test suite for the Token contract
3
+
4
+ module Tests where
5
+
6
+ import Daml.Script
7
+ import Token
8
+
9
+ -- Test: Token creation
10
+ testTokenCreation : Script ()
11
+ testTokenCreation = script do
12
+ alice <- allocateParty "Alice"
13
+ issuer <- allocateParty "Issuer"
14
+
15
+ token <- submit issuer do
16
+ createCmd Token with
17
+ issuer = issuer
18
+ owner = alice
19
+ amount = 100.0
20
+ symbol = "TEST"
21
+
22
+ Some tokenData <- queryContractId alice token
23
+ assert (tokenData.amount == 100.0)
24
+ assert (tokenData.symbol == "TEST")
25
+
26
+ -- Test: Token transfer
27
+ testTokenTransfer : Script ()
28
+ testTokenTransfer = script do
29
+ alice <- allocateParty "Alice"
30
+ bob <- allocateParty "Bob"
31
+ issuer <- allocateParty "Issuer"
32
+
33
+ token <- submit issuer do
34
+ createCmd Token with
35
+ issuer = issuer
36
+ owner = alice
37
+ amount = 50.0
38
+ symbol = "TEST"
39
+
40
+ newToken <- submit alice do
41
+ exerciseCmd token Transfer with newOwner = bob
42
+
43
+ Some tokenData <- queryContractId bob newToken
44
+ assert (tokenData.owner == bob)
45
+ assert (tokenData.amount == 50.0)
46
+
47
+ -- Test: Token split
48
+ testTokenSplit : Script ()
49
+ testTokenSplit = script do
50
+ alice <- allocateParty "Alice"
51
+ issuer <- allocateParty "Issuer"
52
+
53
+ token <- submit issuer do
54
+ createCmd Token with
55
+ issuer = issuer
56
+ owner = alice
57
+ amount = 100.0
58
+ symbol = "TEST"
59
+
60
+ (token1, token2) <- submit alice do
61
+ exerciseCmd token Split with splitAmount = 30.0
62
+
63
+ Some data1 <- queryContractId alice token1
64
+ Some data2 <- queryContractId alice token2
65
+
66
+ assert (data1.amount == 30.0)
67
+ assert (data2.amount == 70.0)
@@ -0,0 +1,56 @@
1
+ -- Token.daml
2
+ -- A simple fungible token on Canton Network
3
+
4
+ module Token where
5
+
6
+ import Daml.Script
7
+
8
+ -- The Token template represents a token holding
9
+ template Token
10
+ with
11
+ issuer : Party
12
+ owner : Party
13
+ amount : Decimal
14
+ symbol : Text
15
+ where
16
+ signatory issuer
17
+ observer owner
18
+
19
+ -- Transfer tokens to another party
20
+ choice Transfer : ContractId Token
21
+ with
22
+ newOwner : Party
23
+ controller owner
24
+ do
25
+ create this with owner = newOwner
26
+
27
+ -- Split tokens (for partial transfers)
28
+ choice Split : (ContractId Token, ContractId Token)
29
+ with
30
+ splitAmount : Decimal
31
+ controller owner
32
+ do
33
+ assertMsg "Split amount must be less than total" (splitAmount < amount)
34
+ token1 <- create this with amount = splitAmount
35
+ token2 <- create this with amount = amount - splitAmount
36
+ return (token1, token2)
37
+
38
+ -- Example: How to use this in practice
39
+ setup : Script ()
40
+ setup = script do
41
+ alice <- allocateParty "Alice"
42
+ bob <- allocateParty "Bob"
43
+ issuer <- allocateParty "TokenIssuer"
44
+
45
+ aliceToken <- submit issuer do
46
+ createCmd Token with
47
+ issuer = issuer
48
+ owner = alice
49
+ amount = 1000.0
50
+ symbol = "TEST"
51
+
52
+ submit alice do
53
+ exerciseCmd aliceToken Transfer with
54
+ newOwner = bob
55
+
56
+ return ()