Skip to content

Commit cbdd368

Browse files
committed
Split canonical and acceptance examples
1 parent 76b94c2 commit cbdd368

26 files changed

Lines changed: 3163 additions & 84 deletions

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
- Add lock-boundary surface syntax for `protected` Cell parameters, `witness`
1818
data parameters, and `require` fail-closed predicates; reserve `lock_args`
1919
until explicit CKB script-args binding is implemented.
20+
- Split bundled examples into clean business examples and profiled acceptance
21+
examples, so scheduler/effect hints stay in release evidence without
22+
crowding the canonical teaching surface.
2023
- Refresh LSP completions and the VS Code grammar/snippets for the new
2124
lock-boundary syntax.
2225

‎docs/CELLSCRIPT_0_13_RELEASE_NOTES_DRAFT.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ New in 0.13 branch work:
3333
`Vec<Hash>` helper usage without implying full `HashMap<K, V>` support. It is
3434
a compiler/tooling language example, not part of the seven-example CKB
3535
production action acceptance matrix.
36+
- The canonical business examples are now mirrored under `examples/business/`,
37+
while production/profile metadata lives under `examples/acceptance/`. The CKB
38+
acceptance script compiles the profiled copies when present, keeping
39+
`#[effect(...)]` and `#[scheduler_hint(...)]` out of beginner-facing files
40+
without dropping release evidence. Subdirectory copies use scoped module
41+
namespaces so they can coexist with the canonical top-level examples during
42+
module loading.
3643
- Runtime and constraints metadata expose each checked stack-backed
3744
fixed-width `Vec<T>` instantiation, including scope, element type/width,
3845
backing capacity, status, and helper set. Constructor helpers now preserve

‎docs/CELLSCRIPT_0_13_TODOLIST.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ Notes:
171171
- [x] builder-backed on-chain lock valid-spend and invalid-spend matrix for
172172
all bundled production locks. The matrix is now part of the CKB production
173173
acceptance report instead of a compile-only claim.
174+
- [x] split examples by audience: clean canonical business examples live in
175+
`examples/business` and flat `examples/*.cell`; profiled production
176+
acceptance copies live in `examples/acceptance`; `registry.cell` is mirrored
177+
under `examples/language`.
174178

175179
---
176180

‎docs/CELLSCRIPT_SURFACE_ELEGANCE_RFC.md‎

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -389,13 +389,22 @@ examples/language/
389389
bounded_vec.cell
390390
391391
examples/acceptance/
392-
token.acceptance.cell
393-
timelock.acceptance.cell
394-
multisig.acceptance.cell
392+
token.cell
393+
amm_pool.cell
394+
launch.cell
395+
vesting.cell
396+
nft.cell
397+
multisig.cell
398+
timelock.cell
395399
```
396400

397-
The current flat `examples/` directory may remain until tooling and acceptance
398-
scripts are ready for the split.
401+
The flat `examples/*.cell` files remain as compatibility mirrors of the
402+
canonical business examples. CKB production acceptance compiles
403+
`examples/acceptance/*.cell` when that directory is present, so scheduler and
404+
effect-profile metadata can stay out of beginner-facing business files.
405+
Subdirectory copies use scoped module namespaces (`cellscript::business::*`,
406+
`cellscript::acceptance::*`, and `cellscript::language::*`) so they can coexist
407+
with the canonical top-level modules during compiler module loading.
399408

400409
## Canonical Style Example
401410

@@ -445,7 +454,7 @@ This list is the living implementation tracker for the RFC.
445454
| `protects T { self ... }` sugar | Deferred | Wait until `self` binding and lock-group aggregation semantics are exact. |
446455
| Hidden sighash defaults | Rejected | Digest mode and signature scope must be visible. |
447456
| Implicit `Address` as signer | Rejected | Address values do not become authorization proofs by name. |
448-
| Business/language/acceptance example directory split | Deferred | Keep flat `examples/` until scripts and package tooling are ready. |
457+
| Business/language/acceptance example directory split | Done | `examples/business` holds clean canonical examples, `examples/acceptance` holds production/profiled examples, and `examples/language` holds `registry.cell`. Flat `examples/*.cell` remains a compatibility mirror for existing commands. |
449458
| `examples/canonical_style.cell` | Not started | Should become the idiomatic reference once pending syntax is available. |
450459
| Action production acceptance | Done | Existing bundled action acceptance remains builder-backed. |
451460
| Lock valid-spend and invalid-spend matrix | Done | Existing bundled locks are exercised through builder-backed local CKB transactions. |

‎docs/wiki/Tutorial-06-Metadata-Verification-and-Production-Gates.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ valid-spend and invalid-spend matrices, valid transaction dry-runs, committed
143143
valid transactions, malformed rejection, measured cycles, consensus-serialized tx
144144
size, occupied-capacity evidence, no under-capacity outputs, all seven production
145145
bundled examples deployed, and a passed final production hardening gate.
146+
The production gate compiles `examples/acceptance/*.cell` when present; those
147+
files intentionally retain scheduler and effect-profile metadata while
148+
`examples/*.cell` and `examples/business/*.cell` remain the cleaner business
149+
reading surface.
146150
Lock behavior coverage is machine-readable through
147151
`lock_acceptance_scope.onchain_lock_spend_matrix_scope`; each listed lock must
148152
have both valid-spend and invalid-spend evidence.

‎docs/wiki/Tutorial-08-Bundled-Example-Contracts.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ CellScript language surface and the current production gates.
1919
| `examples/amm_pool.cell` | Shared pool state, swap, and liquidity effects. |
2020
| `examples/launch.cell` | Launch/pool composition patterns. |
2121

22+
The top-level `examples/*.cell` files are the canonical business reading
23+
surface. `examples/business/*.cell` mirrors that clean surface explicitly.
24+
`examples/acceptance/*.cell` carries production/profile metadata such as
25+
`#[effect(...)]` and `#[scheduler_hint(...)]`; the CKB acceptance script uses
26+
those profiled copies when generating release evidence. Subdirectory copies use
27+
`cellscript::business::*` and `cellscript::acceptance::*` module namespaces so
28+
they can coexist with the top-level examples during module loading.
29+
2230
`examples/registry.cell` is intentionally outside this seven-example production
2331
matrix. It is a 0.13 bounded-collection language example for local
2432
`Vec<Address>` and `Vec<Hash>` helpers, covered by compiler/tooling tests rather

‎examples/acceptance/amm_pool.cell‎

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// amm_pool.cell - Constant Product AMM Liquidity Pool
2+
3+
module cellscript::acceptance::amm_pool
4+
5+
use cellscript::fungible_token::Token
6+
7+
/// LP (Liquidity Provider) receipt
8+
receipt LPReceipt has store {
9+
pool_id: Hash
10+
lp_amount: u64
11+
provider: Address
12+
}
13+
14+
shared Pool has store {
15+
token_a_symbol: [u8; 8]
16+
token_b_symbol: [u8; 8]
17+
reserve_a: u64
18+
reserve_b: u64
19+
total_lp: u64
20+
fee_rate_bps: u16
21+
}
22+
23+
action seed_pool(
24+
token_a: Token,
25+
token_b: Token,
26+
fee_rate_bps: u16,
27+
provider: Address
28+
) -> (Pool, LPReceipt) {
29+
assert_invariant(token_a.symbol != token_b.symbol, "same token")
30+
assert_invariant(token_a.amount > 0 && token_b.amount > 0, "zero liquidity")
31+
assert_invariant(fee_rate_bps <= 10000, "fee too high")
32+
33+
let initial_lp = isqrt(token_a.amount * token_b.amount)
34+
35+
consume token_a
36+
consume token_b
37+
38+
let pool = create Pool {
39+
token_a_symbol: token_a.symbol,
40+
token_b_symbol: token_b.symbol,
41+
reserve_a: token_a.amount,
42+
reserve_b: token_b.amount,
43+
total_lp: initial_lp,
44+
fee_rate_bps
45+
}
46+
47+
let receipt = create LPReceipt {
48+
pool_id: pool.type_hash(),
49+
lp_amount: initial_lp,
50+
provider
51+
} with_lock(provider)
52+
53+
(pool, receipt)
54+
}
55+
56+
action swap_a_for_b(
57+
pool: &mut Pool,
58+
input: Token,
59+
min_output: u64,
60+
to: Address
61+
) -> Token {
62+
assert_invariant(input.symbol == pool.token_a_symbol, "wrong input token")
63+
64+
let fee = input.amount * pool.fee_rate_bps as u64 / 10000
65+
let net_input = input.amount - fee
66+
67+
let output = pool.reserve_b * net_input / (pool.reserve_a + net_input)
68+
69+
assert_invariant(output >= min_output, "slippage exceeded")
70+
assert_invariant(output < pool.reserve_b, "insufficient reserves")
71+
72+
consume input
73+
74+
pool.reserve_a = pool.reserve_a + input.amount
75+
pool.reserve_b = pool.reserve_b - output
76+
77+
create Token {
78+
amount: output,
79+
symbol: pool.token_b_symbol
80+
} with_lock(to)
81+
}
82+
83+
action add_liquidity(
84+
pool: &mut Pool,
85+
token_a: Token,
86+
token_b: Token,
87+
provider: Address
88+
) -> LPReceipt {
89+
assert_invariant(token_a.symbol == pool.token_a_symbol, "wrong token a")
90+
assert_invariant(token_b.symbol == pool.token_b_symbol, "wrong token b")
91+
92+
let lp_from_a = token_a.amount * pool.total_lp / pool.reserve_a
93+
let lp_from_b = token_b.amount * pool.total_lp / pool.reserve_b
94+
let lp_amount = min(lp_from_a, lp_from_b)
95+
96+
consume token_a
97+
consume token_b
98+
99+
pool.reserve_a = pool.reserve_a + token_a.amount
100+
pool.reserve_b = pool.reserve_b + token_b.amount
101+
pool.total_lp = pool.total_lp + lp_amount
102+
103+
create LPReceipt {
104+
pool_id: pool.type_hash(),
105+
lp_amount,
106+
provider
107+
} with_lock(provider)
108+
}
109+
110+
action remove_liquidity(
111+
pool: &mut Pool,
112+
receipt: LPReceipt,
113+
provider: Address
114+
) -> (Token, Token) {
115+
assert_invariant(receipt.pool_id == pool.type_hash(), "wrong pool")
116+
117+
let amount_a = receipt.lp_amount * pool.reserve_a / pool.total_lp
118+
let amount_b = receipt.lp_amount * pool.reserve_b / pool.total_lp
119+
120+
consume receipt
121+
122+
pool.reserve_a = pool.reserve_a - amount_a
123+
pool.reserve_b = pool.reserve_b - amount_b
124+
pool.total_lp = pool.total_lp - receipt.lp_amount
125+
126+
let token_a = create Token {
127+
amount: amount_a,
128+
symbol: pool.token_a_symbol
129+
} with_lock(provider)
130+
131+
let token_b = create Token {
132+
amount: amount_b,
133+
symbol: pool.token_b_symbol
134+
} with_lock(provider)
135+
136+
(token_a, token_b)
137+
}
138+
139+
action isqrt(n: u64) -> u64 {
140+
if n == 0 {
141+
return 0
142+
}
143+
144+
let mut x = n
145+
let mut y = (x + 1) / 2
146+
147+
while y < x {
148+
x = y
149+
y = (x + n / x) / 2
150+
}
151+
152+
x
153+
}
154+
155+
action min(a: u64, b: u64) -> u64 {
156+
if a < b { a } else { b }
157+
}

‎examples/acceptance/launch.cell‎

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// launch.cell - Token Launch and Pool Seeding
2+
3+
module cellscript::acceptance::launch
4+
5+
use cellscript::fungible_token::{Token, MintAuthority}
6+
use cellscript::amm_pool::{Pool, LPReceipt, seed_pool}
7+
8+
/// Launch a new token and seed liquidity pool
9+
action launch_token(
10+
symbol: [u8; 8],
11+
max_supply: u64,
12+
initial_mint: u64,
13+
pool_seed_amount: u64,
14+
pool_paired_token: Token,
15+
fee_rate_bps: u16,
16+
creator: Address,
17+
distribution: [(Address, u64); 4]
18+
) -> (MintAuthority, Pool, LPReceipt) {
19+
assert_invariant(initial_mint <= max_supply, "initial exceeds max")
20+
assert_invariant(pool_seed_amount <= initial_mint, "pool seed exceeds mint")
21+
22+
// Calculate distribution total
23+
let dist_total = distribution[0].1 + distribution[1].1
24+
+ distribution[2].1 + distribution[3].1
25+
assert_invariant(dist_total + pool_seed_amount <= initial_mint,
26+
"allocation exceeds mint")
27+
28+
// Create mint authority
29+
let auth = create MintAuthority {
30+
token_symbol: symbol,
31+
max_supply,
32+
minted: initial_mint
33+
} with_lock(creator)
34+
35+
// Create distribution tokens
36+
for (addr, amount) in distribution {
37+
if amount > 0 {
38+
create Token {
39+
amount,
40+
symbol
41+
} with_lock(addr)
42+
}
43+
}
44+
45+
// Create pool seed token
46+
let pool_token = create Token {
47+
amount: pool_seed_amount,
48+
symbol
49+
} with_lock(creator)
50+
51+
// Call pool pattern helper to seed the pool
52+
let (pool, lp_receipt) = seed_pool(
53+
pool_token,
54+
pool_paired_token,
55+
fee_rate_bps,
56+
creator
57+
)
58+
59+
(auth, pool, lp_receipt)
60+
}
61+
62+
/// Simple token launch (without liquidity pool)
63+
action simple_launch(
64+
symbol: [u8; 8],
65+
max_supply: u64,
66+
initial_mint: u64,
67+
creator: Address,
68+
recipients: [(Address, u64); 8]
69+
) -> MintAuthority {
70+
assert_invariant(initial_mint <= max_supply, "initial exceeds max")
71+
72+
// Calculate distribution total
73+
let mut total_distributed: u64 = 0
74+
for (_, amount) in recipients {
75+
total_distributed = total_distributed + amount
76+
}
77+
assert_invariant(total_distributed <= initial_mint, "distribution exceeds mint")
78+
79+
// Create mint authority
80+
let auth = create MintAuthority {
81+
token_symbol: symbol,
82+
max_supply,
83+
minted: initial_mint
84+
} with_lock(creator)
85+
86+
// Distribute tokens
87+
for (addr, amount) in recipients {
88+
if amount > 0 {
89+
create Token {
90+
amount,
91+
symbol
92+
} with_lock(addr)
93+
}
94+
}
95+
96+
// Remaining tokens go to creator
97+
let remaining = initial_mint - total_distributed
98+
if remaining > 0 {
99+
create Token {
100+
amount: remaining,
101+
symbol
102+
} with_lock(creator)
103+
}
104+
105+
auth
106+
}

0 commit comments

Comments
 (0)