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 +51 -0
- package/bin/create-canton-app.js +44 -0
- package/package.json +32 -0
- package/src/commands/compile.js +39 -0
- package/src/commands/create.js +163 -0
- package/src/commands/test.js +41 -0
- package/src/templates/empty/daml/Main.daml +22 -0
- package/src/templates/escrow/daml/Escrow.daml +53 -0
- package/src/templates/token/daml/Tests.daml +67 -0
- package/src/templates/token/daml/Token.daml +56 -0
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 ()
|