@0xobelisk/sui-common 1.2.0-pre.99 → 2.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.
Files changed (39) hide show
  1. package/LICENSE +92 -0
  2. package/README.md +670 -1
  3. package/dist/index.d.ts +61 -27
  4. package/dist/index.js +1078 -461
  5. package/dist/index.js.map +1 -1
  6. package/package.json +15 -17
  7. package/src/codegen/debug.ts +0 -4
  8. package/src/codegen/types/index.ts +46 -10
  9. package/src/codegen/utils/config.ts +1 -1
  10. package/src/codegen/utils/format.ts +0 -6
  11. package/src/codegen/utils/formatAndWrite.ts +10 -31
  12. package/src/codegen/utils/generateLock.ts +122 -0
  13. package/src/codegen/utils/index.ts +4 -2
  14. package/src/codegen/utils/renderMove/{schemaGen.ts → codegen.ts} +40 -28
  15. package/src/codegen/utils/renderMove/common.ts +0 -65
  16. package/src/codegen/utils/renderMove/dapp.ts +2 -14
  17. package/src/codegen/utils/renderMove/generateDappKey.ts +33 -18
  18. package/src/codegen/utils/renderMove/generateError.ts +32 -15
  19. package/src/codegen/utils/renderMove/generateGenesis.ts +55 -22
  20. package/src/codegen/utils/renderMove/generateInitTest.ts +26 -14
  21. package/src/codegen/utils/renderMove/generateObjects.ts +377 -0
  22. package/src/codegen/utils/renderMove/generatePermits.ts +151 -0
  23. package/src/codegen/utils/renderMove/generateResources.ts +894 -242
  24. package/src/codegen/utils/renderMove/generateScenes.ts +467 -0
  25. package/src/codegen/utils/renderMove/generateScript.ts +18 -13
  26. package/src/codegen/utils/renderMove/generateSystem.ts +0 -2
  27. package/src/codegen/utils/renderMove/generateUserStorageInit.ts +37 -0
  28. package/src/codegen/utils/validateConfig.ts +237 -0
  29. package/src/index.ts +0 -1
  30. package/src/modules.d.ts +0 -10
  31. package/src/codegen/modules.d.ts +0 -1
  32. package/src/codegen/utils/posixPath.ts +0 -8
  33. package/src/codegen/utils/renderMove/generateComponents.ts +0 -802
  34. package/src/codegen/utils/renderMove/generateDefaultSchema.ts +0 -216
  35. package/src/codegen/utils/renderMove/generateEvent.ts +0 -99
  36. package/src/codegen/utils/renderMove/generateSchema.ts +0 -287
  37. package/src/codegen/utils/renderMove/generateSchemaHub.ts +0 -60
  38. package/src/parseData/index.ts +0 -1
  39. package/src/parseData/parser/index.ts +0 -47
@@ -0,0 +1,467 @@
1
+ import { DubheConfig, SceneConfig, Component } from '../../types';
2
+ import { formatAndWriteMove } from '../formatAndWrite';
3
+ import path from 'node:path';
4
+
5
+ function toPascalCase(str: string): string {
6
+ return str
7
+ .split('_')
8
+ .map((word) => {
9
+ if (/^\d+$/.test(word)) return word;
10
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
11
+ })
12
+ .join('');
13
+ }
14
+
15
+ function getMoveType(t: string): string {
16
+ return t === 'string' || t === 'String' ? 'String' : t;
17
+ }
18
+
19
+ function sceneStorageType(markerName: string): string {
20
+ return `dubhe::dapp_service::SceneStorage<${markerName}>`;
21
+ }
22
+
23
+ function permitMarker(config: DubheConfig, cfg: SceneConfig): string | undefined {
24
+ if (cfg.authorization.kind !== 'permit') return undefined;
25
+ return `${config.name}::${cfg.authorization.permit}::${toPascalCase(cfg.authorization.permit)}`;
26
+ }
27
+
28
+ function permitType(config: DubheConfig, cfg: SceneConfig): string | undefined {
29
+ if (cfg.authorization.kind !== 'permit') return undefined;
30
+ return `dubhe::dapp_service::ScenePermit<${permitMarker(config, cfg)}>`;
31
+ }
32
+
33
+ function generateFieldAccessors(config: DubheConfig, sceneKey: string, cfg: SceneConfig): string {
34
+ const markerName = toPascalCase(sceneKey);
35
+ const storageType = sceneStorageType(markerName);
36
+ const permit = permitMarker(config, cfg);
37
+ const permitStorageType = permitType(config, cfg);
38
+ const lines: string[] = [];
39
+
40
+ for (const [fieldName, fieldType] of Object.entries(cfg.fields)) {
41
+ const moveType = getMoveType(fieldType as string);
42
+
43
+ if (cfg.authorization.kind === 'permit' && permit && permitStorageType) {
44
+ lines.push(`
45
+ public fun get_${fieldName}(storage: &${storageType}): ${moveType} {
46
+ dubhe::dapp_system::get_scene_field<${markerName}, ${moveType}>(storage, b"${fieldName}")
47
+ }
48
+
49
+ public(package) fun set_${fieldName}(
50
+ permit: &${permitStorageType},
51
+ storage: &mut ${storageType},
52
+ user_storage: &dubhe::dapp_service::UserStorage,
53
+ value: ${moveType},
54
+ ctx: &TxContext,
55
+ ) {
56
+ dubhe::dapp_system::set_scene_field<DappKey, ${permit}, ${markerName}, ${moveType}>(
57
+ dapp_key::new(), permit, storage, user_storage, b"${fieldName}", value, ctx
58
+ );
59
+ }
60
+
61
+ public(package) fun remove_${fieldName}_system_maintenance(storage: &mut ${storageType}): ${moveType} {
62
+ dubhe::dapp_system::remove_scene_field_system_maintenance<DappKey, ${markerName}, ${moveType}>(
63
+ dapp_key::new(), storage, b"${fieldName}"
64
+ )
65
+ }`);
66
+ } else {
67
+ lines.push(`
68
+ public fun get_${fieldName}(storage: &${storageType}): ${moveType} {
69
+ dubhe::dapp_system::get_scene_field<${markerName}, ${moveType}>(storage, b"${fieldName}")
70
+ }
71
+
72
+ public(package) fun set_${fieldName}(storage: &mut ${storageType}, value: ${moveType}) {
73
+ dubhe::dapp_system::set_scene_field_system<DappKey, ${markerName}, ${moveType}>(
74
+ dapp_key::new(), storage, b"${fieldName}", value
75
+ );
76
+ }
77
+
78
+ public(package) fun remove_${fieldName}(storage: &mut ${storageType}): ${moveType} {
79
+ dubhe::dapp_system::remove_scene_field_system_maintenance<DappKey, ${markerName}, ${moveType}>(
80
+ dapp_key::new(), storage, b"${fieldName}"
81
+ )
82
+ }`);
83
+ }
84
+ }
85
+
86
+ return lines.join('\n');
87
+ }
88
+
89
+ function writeArgs(config: DubheConfig, cfg: SceneConfig): string {
90
+ const type = permitType(config, cfg);
91
+ if (cfg.authorization.kind !== 'permit' || !type) return '';
92
+ return ` permit: &${type},\n`;
93
+ }
94
+
95
+ /// Permit-authorized writes identify the caller by user_storage's canonical owner.
96
+ function userStorageArg(cfg: SceneConfig): string {
97
+ if (cfg.authorization.kind !== 'permit') return '';
98
+ return ` user_storage: &dubhe::dapp_service::UserStorage,\n`;
99
+ }
100
+
101
+ function writeCtxArg(cfg: SceneConfig): string {
102
+ return cfg.authorization.kind === 'permit' ? ' ctx: &TxContext,\n' : '';
103
+ }
104
+
105
+ function setFieldCall(
106
+ config: DubheConfig,
107
+ cfg: SceneConfig,
108
+ markerName: string,
109
+ moveType: string,
110
+ fieldExpr: string,
111
+ valueExpr: string
112
+ ): string {
113
+ const permit = permitMarker(config, cfg);
114
+ if (cfg.authorization.kind === 'permit' && permit) {
115
+ return `dubhe::dapp_system::set_scene_field<DappKey, ${permit}, ${markerName}, ${moveType}>(
116
+ dapp_key::new(), permit, storage, user_storage, ${fieldExpr}, ${valueExpr}, ctx
117
+ );`;
118
+ }
119
+ return `dubhe::dapp_system::set_scene_field_system<DappKey, ${markerName}, ${moveType}>(
120
+ dapp_key::new(), storage, ${fieldExpr}, ${valueExpr}
121
+ );`;
122
+ }
123
+
124
+ function removeFieldCall(
125
+ config: DubheConfig,
126
+ cfg: SceneConfig,
127
+ markerName: string,
128
+ moveType: string,
129
+ fieldExpr: string
130
+ ): string {
131
+ const permit = permitMarker(config, cfg);
132
+ if (cfg.authorization.kind === 'permit' && permit) {
133
+ return `dubhe::dapp_system::remove_scene_field<DappKey, ${permit}, ${markerName}, ${moveType}>(
134
+ dapp_key::new(), permit, storage, user_storage, ${fieldExpr}, ctx
135
+ )`;
136
+ }
137
+ return `dubhe::dapp_system::remove_scene_field_system_maintenance<DappKey, ${markerName}, ${moveType}>(
138
+ dapp_key::new(), storage, ${fieldExpr}
139
+ )`;
140
+ }
141
+
142
+ function generateFungibleBagAccessors(
143
+ config: DubheConfig,
144
+ sceneKey: string,
145
+ cfg: SceneConfig,
146
+ resourceName: string
147
+ ): string {
148
+ const markerName = toPascalCase(sceneKey);
149
+ const storageType = sceneStorageType(markerName);
150
+
151
+ return `
152
+ public fun get_${resourceName}(storage: &${storageType}): u64 {
153
+ if (dubhe::dapp_system::has_scene_field<${markerName}, u64>(storage, b"${resourceName}")) {
154
+ dubhe::dapp_system::get_scene_field<${markerName}, u64>(storage, b"${resourceName}")
155
+ } else { 0 }
156
+ }
157
+
158
+ public(package) fun add_${resourceName}(
159
+ ${writeArgs(config, cfg)} storage: &mut ${storageType},
160
+ ${userStorageArg(cfg)} amount: u64,
161
+ ${writeCtxArg(cfg)} ) {
162
+ let current = get_${resourceName}(storage);
163
+ ${setFieldCall(config, cfg, markerName, 'u64', `b"${resourceName}"`, 'current + amount')}
164
+ }
165
+
166
+ public(package) fun sub_${resourceName}(
167
+ ${writeArgs(config, cfg)} storage: &mut ${storageType},
168
+ ${userStorageArg(cfg)} amount: u64,
169
+ ${writeCtxArg(cfg)} ) {
170
+ let current = get_${resourceName}(storage);
171
+ assert!(current >= amount, EInsufficientAmount);
172
+ ${setFieldCall(config, cfg, markerName, 'u64', `b"${resourceName}"`, 'current - amount')}
173
+ }`;
174
+ }
175
+
176
+ function generateKeyedBagAccessors(
177
+ config: DubheConfig,
178
+ sceneKey: string,
179
+ cfg: SceneConfig,
180
+ resourceName: string,
181
+ idField: string
182
+ ): string {
183
+ const markerName = toPascalCase(sceneKey);
184
+ const storageType = sceneStorageType(markerName);
185
+
186
+ return `
187
+ public fun has_${resourceName}(storage: &${storageType}, ${idField}: u64): bool {
188
+ let key = sui::bcs::to_bytes(&${idField});
189
+ dubhe::dapp_system::has_scene_field<${markerName}, vector<u8>>(storage, key)
190
+ }
191
+
192
+ public fun get_${resourceName}_data(storage: &${storageType}, ${idField}: u64): vector<u8> {
193
+ let key = sui::bcs::to_bytes(&${idField});
194
+ dubhe::dapp_system::get_scene_field<${markerName}, vector<u8>>(storage, key)
195
+ }
196
+
197
+ public(package) fun set_${resourceName}_data(
198
+ ${writeArgs(config, cfg)} storage: &mut ${storageType},
199
+ ${userStorageArg(cfg)} ${idField}: u64,
200
+ data: vector<u8>,
201
+ ${writeCtxArg(cfg)} ) {
202
+ let key = sui::bcs::to_bytes(&${idField});
203
+ assert!(!dubhe::dapp_system::has_scene_field<${markerName}, vector<u8>>(storage, key), EDuplicateItemId);
204
+ ${setFieldCall(config, cfg, markerName, 'vector<u8>', 'key', 'data')}
205
+ }
206
+
207
+ public(package) fun remove_${resourceName}_data(
208
+ ${writeArgs(config, cfg)} storage: &mut ${storageType},
209
+ ${userStorageArg(cfg)} ${idField}: u64,
210
+ ${writeCtxArg(cfg)} ): vector<u8> {
211
+ let key = sui::bcs::to_bytes(&${idField});
212
+ assert!(dubhe::dapp_system::has_scene_field<${markerName}, vector<u8>>(storage, key), EFieldNotFound);
213
+ ${removeFieldCall(config, cfg, markerName, 'vector<u8>', 'key')}
214
+ }`;
215
+ }
216
+
217
+ function generateAcceptsFromTransfers(
218
+ projectName: string,
219
+ destKey: string,
220
+ destCfg: SceneConfig,
221
+ destAccepts: string[],
222
+ acceptsFrom: string[],
223
+ config: DubheConfig
224
+ ): { imports: string[]; functions: string[] } {
225
+ const resources = config.resources ?? {};
226
+ const allObjects = config.objects ?? {};
227
+ const allScenes = config.scenes ?? {};
228
+ const destMarker = toPascalCase(destKey);
229
+ const destStorageType = sceneStorageType(destMarker);
230
+ const imports: string[] = [];
231
+ const functions: string[] = [];
232
+ const destPermit = permitType(config, destCfg);
233
+ const destPermitArg =
234
+ destCfg.authorization.kind === 'permit' && destPermit
235
+ ? ` dest_permit: &${destPermit},\n`
236
+ : '';
237
+ const destCallPrefix = destCfg.authorization.kind === 'permit' ? 'dest_permit, ' : '';
238
+ const destCtxCall = destCfg.authorization.kind === 'permit' ? ', ctx' : '';
239
+
240
+ for (const sourceName of acceptsFrom) {
241
+ const sourceCfg = allObjects[sourceName] ?? allScenes[sourceName];
242
+ if (!sourceCfg) continue;
243
+
244
+ const sourceAccepts = sourceCfg.accepts ?? [];
245
+ const sourceMarker = toPascalCase(sourceName);
246
+ imports.push(` use ${projectName}::${sourceName};`);
247
+
248
+ const isSourceScene = !!allScenes[sourceName];
249
+ const qualifiedSourceMarker = `${projectName}::${sourceName}::${sourceMarker}`;
250
+ const sourceStorageType = isSourceScene
251
+ ? `dubhe::dapp_service::SceneStorage<${qualifiedSourceMarker}>`
252
+ : `dubhe::dapp_service::ObjectStorage<${qualifiedSourceMarker}>`;
253
+ const sceneSourceCfg = isSourceScene ? (sourceCfg as SceneConfig) : undefined;
254
+ const sourcePermit = sceneSourceCfg ? permitType(config, sceneSourceCfg) : undefined;
255
+ const sourceIsPermitScene = sceneSourceCfg?.authorization.kind === 'permit' && !!sourcePermit;
256
+ const sourcePermitArg = sourceIsPermitScene ? ` source_permit: &${sourcePermit},\n` : '';
257
+ const sourceCallPrefix = sourceIsPermitScene ? 'source_permit, ' : '';
258
+ const sourceUserStorageCall = sourceIsPermitScene ? 'user_storage, ' : '';
259
+ const sourceCtxCall = sourceIsPermitScene ? ', ctx' : '';
260
+
261
+ const destIsPermitScene = destCfg.authorization.kind === 'permit';
262
+ const destUserStorageCall = destIsPermitScene ? 'user_storage, ' : '';
263
+ // Permit-authorized writes resolve the caller identity from user_storage.
264
+ const userStorageParam =
265
+ sourceIsPermitScene || destIsPermitScene
266
+ ? ` user_storage: &dubhe::dapp_service::UserStorage,\n`
267
+ : '';
268
+ const ctxParam =
269
+ sourceIsPermitScene || destIsPermitScene ? ' ctx: &TxContext,\n' : '';
270
+
271
+ const commonResources = sourceAccepts.filter((r) => destAccepts.includes(r));
272
+ for (const resourceName of commonResources) {
273
+ const resCfg = resources[resourceName];
274
+ if (!resCfg || typeof resCfg === 'string') continue;
275
+ const comp = resCfg as Component;
276
+
277
+ if (!comp.fungible && comp.keys?.length) {
278
+ const idField = comp.keys[0];
279
+ functions.push(`
280
+ public(package) fun transfer_${sourceName}_to_${destKey}_${resourceName}(
281
+ ${sourcePermitArg}${destPermitArg} from: &mut ${sourceStorageType},
282
+ to: &mut ${destStorageType},
283
+ ${userStorageParam} ${idField}: u64,
284
+ ${ctxParam} ) {
285
+ let data = ${sourceName}::remove_${resourceName}_data(${sourceCallPrefix}from, ${sourceUserStorageCall}${idField}${sourceCtxCall});
286
+ set_${resourceName}_data(${destCallPrefix}to, ${destUserStorageCall}${idField}, data${destCtxCall});
287
+ }`);
288
+ } else {
289
+ functions.push(`
290
+ public(package) fun transfer_${sourceName}_to_${destKey}_${resourceName}(
291
+ ${sourcePermitArg}${destPermitArg} from: &mut ${sourceStorageType},
292
+ to: &mut ${destStorageType},
293
+ ${userStorageParam} amount: u64,
294
+ ${ctxParam} ) {
295
+ ${sourceName}::sub_${resourceName}(${sourceCallPrefix}from, ${sourceUserStorageCall}amount${sourceCtxCall});
296
+ add_${resourceName}(${destCallPrefix}to, ${destUserStorageCall}amount${destCtxCall});
297
+ }`);
298
+ }
299
+ }
300
+ }
301
+
302
+ return { imports, functions };
303
+ }
304
+
305
+ export async function generateScenes(config: DubheConfig, outputDir: string) {
306
+ if (!config.scenes || Object.keys(config.scenes).length === 0) return;
307
+ console.log('\n📦 Starting Scene Storage Generation...');
308
+
309
+ const projectName = config.name;
310
+ const resources = config.resources ?? {};
311
+
312
+ for (const [sceneKey, sceneCfg] of Object.entries(config.scenes)) {
313
+ console.log(` └─ ${sceneKey}`);
314
+ const markerName = toPascalCase(sceneKey);
315
+ const sceneTypeTag = `b"${sceneKey}"`;
316
+ const fullSceneType = sceneStorageType(markerName);
317
+ const fieldAccessors = generateFieldAccessors(config, sceneKey, sceneCfg);
318
+ const acceptedResources = sceneCfg.accepts ?? [];
319
+
320
+ const bagAccessorParts: string[] = [];
321
+ for (const resourceName of acceptedResources) {
322
+ const resCfg = resources[resourceName];
323
+ if (!resCfg || typeof resCfg === 'string') continue;
324
+ const comp = resCfg as Component;
325
+ if (!comp.fungible && comp.keys?.length) {
326
+ bagAccessorParts.push(
327
+ generateKeyedBagAccessors(config, sceneKey, sceneCfg, resourceName, comp.keys[0])
328
+ );
329
+ } else {
330
+ bagAccessorParts.push(
331
+ generateFungibleBagAccessors(config, sceneKey, sceneCfg, resourceName)
332
+ );
333
+ }
334
+ }
335
+
336
+ const { imports: afImports, functions: afFunctions } = generateAcceptsFromTransfers(
337
+ projectName,
338
+ sceneKey,
339
+ sceneCfg,
340
+ acceptedResources,
341
+ sceneCfg.acceptsFrom ?? [],
342
+ config
343
+ );
344
+
345
+ const sceneFieldTypes = Object.values(sceneCfg.fields) as string[];
346
+ const sceneNeedsStringImport = sceneFieldTypes.some(
347
+ (t) => t === 'string' || t === 'String' || t === 'vector<String>'
348
+ );
349
+ const sceneStringImport = sceneNeedsStringImport ? `\n use std::ascii::String;` : '';
350
+
351
+ const hasKeyedBagAccessors = acceptedResources.some((resourceName) => {
352
+ const resCfg = resources[resourceName];
353
+ if (!resCfg || typeof resCfg === 'string') return false;
354
+ return !!(resCfg as Component).keys?.length && !(resCfg as Component).fungible;
355
+ });
356
+ const hasFungibleBagAccessors = acceptedResources.some((resourceName) => {
357
+ const resCfg = resources[resourceName];
358
+ if (!resCfg || typeof resCfg === 'string') return false;
359
+ return !!(resCfg as Component).fungible;
360
+ });
361
+
362
+ const errorConstants = [
363
+ hasKeyedBagAccessors
364
+ ? ` #[error]\n const EFieldNotFound: vector<u8> = b"Field not found";`
365
+ : '',
366
+ hasFungibleBagAccessors
367
+ ? ` #[error]\n const EInsufficientAmount: vector<u8> = b"Insufficient amount";`
368
+ : '',
369
+ hasKeyedBagAccessors
370
+ ? ` #[error]\n const EDuplicateItemId: vector<u8> = b"Duplicate item id";`
371
+ : ''
372
+ ]
373
+ .filter(Boolean)
374
+ .join('\n');
375
+ const errorBlock =
376
+ errorConstants.length > 0
377
+ ? `\n // ─── Error constants ───────────────────────────────────────────────────\n${errorConstants}\n`
378
+ : '';
379
+
380
+ const afImportBlock = afImports.length > 0 ? '\n' + afImports.join('\n') : '';
381
+ const permit = permitType(config, sceneCfg);
382
+ const createFns =
383
+ sceneCfg.authorization.kind === 'permit' && permit
384
+ ? `
385
+ public(package) fun new_${sceneKey}_with_permit(
386
+ dapp_storage: &DappStorage,
387
+ permit: &${permit},
388
+ ctx: &mut TxContext,
389
+ ): ${fullSceneType} {
390
+ dubhe::dapp_system::new_typed_scene_with_permit<DappKey, ${permitMarker(
391
+ config,
392
+ sceneCfg
393
+ )}, ${markerName}>(
394
+ dapp_key::new(), dapp_storage, permit, SCENE_TYPE, ctx
395
+ )
396
+ }
397
+
398
+ public(package) fun create_${sceneKey}_with_permit(
399
+ dapp_storage: &DappStorage,
400
+ permit: &${permit},
401
+ ctx: &mut TxContext,
402
+ ) {
403
+ dubhe::dapp_system::create_and_share_typed_scene_with_permit<DappKey, ${permitMarker(
404
+ config,
405
+ sceneCfg
406
+ )}, ${markerName}>(
407
+ dapp_key::new(), dapp_storage, permit, SCENE_TYPE, ctx
408
+ );
409
+ }`
410
+ : `
411
+ public(package) fun new_${sceneKey}_system(
412
+ dapp_storage: &DappStorage,
413
+ ctx: &mut TxContext,
414
+ ): ${fullSceneType} {
415
+ dubhe::dapp_system::new_typed_scene_system<DappKey, ${markerName}>(
416
+ dapp_key::new(), dapp_storage, SCENE_TYPE, ctx
417
+ )
418
+ }
419
+
420
+ public(package) fun create_${sceneKey}_system(
421
+ dapp_storage: &DappStorage,
422
+ ctx: &mut TxContext,
423
+ ) {
424
+ dubhe::dapp_system::create_and_share_typed_scene_system<DappKey, ${markerName}>(
425
+ dapp_key::new(), dapp_storage, SCENE_TYPE, ctx
426
+ );
427
+ }`;
428
+
429
+ const code = `module ${projectName}::${sceneKey} {
430
+ use dubhe::dapp_service::DappStorage;
431
+ use ${projectName}::dapp_key;
432
+ use ${projectName}::dapp_key::DappKey;${sceneStringImport}${afImportBlock}
433
+ ${errorBlock}
434
+ const SCENE_TYPE: vector<u8> = ${sceneTypeTag};
435
+
436
+ /// Phantom type that distinguishes this scene storage at compile time.
437
+ public struct ${markerName} has copy, drop {}
438
+
439
+ // ─── Field accessors (own fields) ──────────────────────────────────────
440
+ ${fieldAccessors}
441
+
442
+ // ─── Bag accessors for accepted resources ─────────────────────────────
443
+ ${bagAccessorParts.join('\n')}
444
+
445
+ // ─── acceptsFrom: cross-storage transfer functions ─────────────────────
446
+ ${afFunctions.join('\n')}
447
+
448
+ // ─── SceneStorage lifecycle wrappers ──────────────────────────────────
449
+ ${createFns}
450
+
451
+ public(package) fun share_${sceneKey}(storage: ${fullSceneType}) {
452
+ dubhe::dapp_system::share_scene_storage<DappKey, ${markerName}>(
453
+ dapp_key::new(), storage
454
+ );
455
+ }
456
+
457
+ public(package) fun destroy_${sceneKey}(storage: ${fullSceneType}) {
458
+ dubhe::dapp_system::destroy_typed_scene<DappKey, ${markerName}>(
459
+ dapp_key::new(), storage
460
+ );
461
+ }
462
+ }
463
+ `;
464
+
465
+ await formatAndWriteMove(code, path.join(outputDir, `${sceneKey}.move`), 'formatAndWriteMove');
466
+ }
467
+ }
@@ -1,23 +1,28 @@
1
1
  import { DubheConfig } from '../../types';
2
2
  import { formatAndWriteMove } from '../formatAndWrite';
3
3
  import { existsSync } from 'fs';
4
- // import { capitalizeAndRemoveUnderscores } from // Unused './generateSchema';
5
4
 
6
- // import { readFileSync } from 'fs'; // Unused
7
-
8
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
9
- function capitalizeFirstLetter(str: string): string {
10
- return str.charAt(0).toUpperCase() + str.slice(1);
11
- }
12
-
13
- export async function generateDeployHook(config: DubheConfig, path: string) {
5
+ export async function generateDeployHook(
6
+ config: DubheConfig,
7
+ path: string,
8
+ initialMode: 0 | 1 = 1
9
+ ) {
14
10
  if (!existsSync(path)) {
15
- const code = `module ${config.name}::deploy_hook {
16
- use dubhe::dapp_service::DappHub;
11
+ const modeComment =
12
+ initialMode === 1
13
+ ? `// Settlement mode: USER_PAYS — users pay transaction fees at settlement time.
14
+ // The framework admin sets the revenue share via set_dapp_revenue_share.
15
+ // Initialise any DappStorage-level defaults here (e.g. resource starting values).`
16
+ : `// Settlement mode: DAPP_SUBSIDIZES — the DApp pays for user operations.
17
+ // Recharge the credit pool via dapp_system::recharge_credit before users can write.
18
+ // Initialise any DappStorage-level defaults here (e.g. resource starting values).`;
17
19
 
18
- public(package) fun run(_dapp_hub: &mut DappHub, _ctx: &mut TxContext) {
20
+ const code = `module ${config.name}::deploy_hook {
21
+ use dubhe::dapp_service::DappStorage;
19
22
 
20
- }
23
+ public(package) fun run(_dapp_storage: &mut DappStorage, _ctx: &mut TxContext) {
24
+ ${modeComment}
25
+ }
21
26
  }`;
22
27
  await formatAndWriteMove(code, path, 'formatAndWriteMove');
23
28
  }
@@ -1,8 +1,6 @@
1
1
  import { DubheConfig } from '../../types';
2
- // import { formatAndWriteMove } from // Unused '../formatAndWrite';
3
2
  import { existsSync } from 'fs';
4
3
  import fs from 'node:fs/promises';
5
- // import path from // Unused 'node:path';
6
4
 
7
5
  export async function generateSystemsAndTests(config: DubheConfig, srcPrefix: string) {
8
6
  if (!existsSync(`${srcPrefix}/src/${config.name}/sources/systems`)) {
@@ -0,0 +1,37 @@
1
+ import { DubheConfig } from '../../types';
2
+ import { formatAndWriteMove } from '../formatAndWrite';
3
+
4
+ /**
5
+ * Generates a `user_storage_init.move` module that provides a single entry-point
6
+ * for end-users to create their `UserStorage` object for the DApp.
7
+ *
8
+ * Only generated for non-dubhe packages; the dubhe framework manages its own
9
+ * internal UserStorage creation through dapp_system::create_user_storage.
10
+ */
11
+ export async function generateUserStorageInit(config: DubheConfig, path: string) {
12
+ if (config.name === 'dubhe') return;
13
+
14
+ const code = `module ${config.name}::user_storage_init {
15
+ use dubhe::dapp_service::{DappHub, DappStorage};
16
+ use dubhe::dapp_system;
17
+ use ${config.name}::dapp_key;
18
+ use ${config.name}::dapp_key::DappKey;
19
+ use ${config.name}::migrate;
20
+
21
+ /// Create a UserStorage for the transaction sender within this DApp.
22
+ /// Must be called once before the user can interact with any user-level resources.
23
+ /// Aborts if the DApp version does not match this package, the DApp is paused,
24
+ /// or the framework version has advanced.
25
+ #[allow(lint(public_entry))]
26
+ public entry fun init_user_storage(
27
+ dapp_hub: &DappHub,
28
+ dapp_storage: &mut DappStorage,
29
+ ctx: &mut TxContext,
30
+ ) {
31
+ dapp_system::ensure_latest_version<DappKey>(dapp_storage, migrate::on_chain_version());
32
+ dapp_system::create_user_storage(dapp_key::new(), dapp_hub, dapp_storage, ctx);
33
+ }
34
+ }
35
+ `;
36
+ await formatAndWriteMove(code, path, 'formatAndWriteMove');
37
+ }