create-dubhe 1.2.0-pre.124 → 1.2.0-pre.125
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/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/101/sui-template/packages/contracts/src/dubhe/Move.lock +0 -6
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/dapp_system.move +82 -42
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/scene_session_test.move +269 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/typed_scene_test.move +25 -9
- package/templates/contract/sui-template/src/dubhe/Move.lock +0 -6
- package/templates/contract/sui-template/src/dubhe/sources/systems/dapp_system.move +82 -42
- package/templates/contract/sui-template/src/dubhe/sources/tests/scene_session_test.move +269 -0
- package/templates/contract/sui-template/src/dubhe/sources/tests/typed_scene_test.move +25 -9
- package/templates/nextjs/sui-card-duel/.gitignore_ +51 -0
- package/templates/nextjs/sui-card-duel/README.md +213 -0
- package/templates/nextjs/sui-card-duel/docker-compose.yml +34 -0
- package/templates/nextjs/sui-card-duel/mprocs.yaml +17 -0
- package/templates/nextjs/sui-card-duel/package.json +43 -0
- package/templates/nextjs/sui-card-duel/packages/client/.gitignore_ +48 -0
- package/templates/nextjs/sui-card-duel/packages/client/.prettierignore +1 -0
- package/templates/nextjs/sui-card-duel/packages/client/.prettierrc.json +8 -0
- package/templates/nextjs/sui-card-duel/packages/client/eslint.config.mjs +22 -0
- package/templates/nextjs/sui-card-duel/packages/client/next.config.mjs +8 -0
- package/templates/nextjs/sui-card-duel/packages/client/package.json +47 -0
- package/templates/nextjs/sui-card-duel/packages/client/postcss.config.mjs +8 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/admin/ArenaPanel.tsx +307 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/admin/page.tsx +622 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/brawl/[id]/page.tsx +559 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/collection/page.tsx +202 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/components/CardView.tsx +66 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/components/GameShell.tsx +294 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/components/icons.tsx +101 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/duel/[id]/page.tsx +477 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/favicon.ico +0 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/globals.css +85 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/hooks/useGame.ts +313 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/layout.tsx +24 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/leaderboard/page.tsx +123 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/lib/game.ts +77 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/lib/scenes.ts +226 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/market/page.tsx +418 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/page.tsx +427 -0
- package/templates/nextjs/sui-card-duel/packages/client/src/app/providers.tsx +69 -0
- package/templates/nextjs/sui-card-duel/packages/client/tailwind.config.ts +21 -0
- package/templates/nextjs/sui-card-duel/packages/client/tsconfig.json +30 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/.gitignore_ +51 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/.prettierignore +1 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/.prettierrc.json +8 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/Dockerfile +5 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/Pub.localnet.toml +13 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/card_duel.lock.json +66 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/docker-compose.yml +34 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/dubhe.config.ts +150 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/logs/combined.log +31383 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/package.json +49 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/config.ts +17 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-brawl.ts +270 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-duel.ts +272 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-lobby.ts +177 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-market.ts +219 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/e2e-session.ts +342 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/query-dapp-revenue.ts +95 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/seed.ts +92 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/scripts/withdraw-dapp-revenue.ts +56 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/Move.toml +15 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/dapp_key.move +46 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/card_kind.move +47 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/match_state.move +37 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/enums/rarity.move +37 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/error.move +126 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/genesis.move +33 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/init_test.move +23 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/objects/arena.move +137 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/permits/brawl_permit.move +125 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/permits/duel_permit.move +125 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/battle_state.move +255 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/card.move +282 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/deck.move +64 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/game_config.move +267 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/gold.move +205 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/resources/profile.move +255 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/scenes/brawl.move +313 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/scenes/duel.move +313 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/codegen/user_storage_init.move +26 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/scripts/deploy_hook.move +32 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/scripts/migrate.move +7 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/arena_system.move +57 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/brawl_system.move +559 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/card_system.move +146 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/duel_system.move +515 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/market_system.move +115 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/systems/player_system.move +37 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/brawl_tests.move +385 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/duel_tests.move +656 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/src/card_duel/sources/tests/test_helpers.move +31 -0
- package/templates/nextjs/sui-card-duel/packages/contracts/sui-indexer-schema.graphql +5973 -0
- package/templates/nextjs/sui-card-duel/pnpm-workspace.yaml +2 -0
- package/templates/nextjs/sui-farm/packages/client/src/app/market/page.tsx +4 -13
- package/templates/nextjs/sui-farm/packages/client/src/app/page.tsx +73 -130
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/codegen/permits/world.move +12 -9
- package/templates/nextjs/sui-farm/packages/contracts/src/harvest/sources/systems/world_system.move +1 -1
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/Move.lock +0 -6
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/dapp_system.move +82 -42
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/scene_session_test.move +269 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/typed_scene_test.move +25 -9
- package/templates/nextjs/sui-farm/packages/client/src/app/hooks/useSessionKey.ts +0 -248
package/dist/cli.js
CHANGED
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bin/cli.ts","../src/config/chains.ts","../package.json","../src/exists.ts"],"sourcesContent":["import { fileURLToPath } from 'node:url';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport glob from 'fast-glob';\nimport yargsInteractive from 'yargs-interactive';\nimport { CHAINS } from '../config/chains';\nimport packageJson from '../../package.json';\nimport { exists } from '../exists';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nconst cwd = process.cwd();\n\nconst init = async () => {\n // Prepare chain options\n const chainChoices = CHAINS.map(({ title, description, value }) => ({\n name: `${title} - ${description}`,\n value\n }));\n\n // Step 1: Choose project name and chain\n const firstStep = await yargsInteractive()\n .usage('$0 [args]')\n .interactive({\n interactive: { default: true },\n projectName: {\n describe: 'Name your project',\n type: 'input'\n },\n chain: {\n describe: 'Pick your chain',\n type: 'list',\n choices: chainChoices.map((c) => c.value)\n },\n dubheVersion: {\n describe: 'The version of Dubhe packages to use, defaults to latest',\n type: 'input',\n default: packageJson.version\n }\n });\n\n const { projectName, chain, dubheVersion } = firstStep;\n if (!projectName) throw new Error('No project name provided.');\n\n // Get available templates based on the selected chain\n const selectedChain = CHAINS.find((c) => c.value === chain);\n if (!selectedChain) {\n throw new Error('Invalid chain selection');\n }\n\n // Prepare platform options\n const platformChoices = selectedChain.supportedTemplates.map(({ title, description, value }) => ({\n name: `${title} - ${description}`,\n value\n }));\n\n // Step 2: Choose platform\n const secondStep = await yargsInteractive()\n .usage('$0 [args]')\n .interactive({\n interactive: { default: true },\n platform: {\n describe: 'Pick your platform',\n type: 'list',\n choices: platformChoices.map((c) => c.value)\n }\n });\n\n const { platform } = secondStep;\n\n const selectedTemplate = selectedChain.supportedTemplates.find((t) => t.value === platform);\n if (!selectedTemplate) {\n throw new Error('Invalid platform selection');\n }\n\n const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);\n const pkgManager = pkgInfo ? pkgInfo.name : 'npm';\n\n const sourceDir = path.join(\n __dirname,\n '..',\n 'templates',\n selectedTemplate.path.replace('{chain}', chain)\n );\n\n if (!(await exists(sourceDir))) {\n throw new Error(`Template directory not found: ${sourceDir}`);\n }\n\n const destDir = path.join(process.cwd(), projectName);\n if (await exists(destDir)) {\n throw new Error(`Target directory \"${destDir}\" already exists.`);\n }\n\n const files = await glob('**/*', { cwd: sourceDir, dot: true });\n\n for (const filename of files) {\n const sourceFile = path.join(sourceDir, filename);\n const destFile = path.join(destDir, filename);\n\n await fs.mkdir(path.dirname(destFile), { recursive: true });\n\n if (/package\\.json$/.test(sourceFile)) {\n const source = await fs.readFile(sourceFile, 'utf-8');\n await fs.writeFile(destFile, source.replaceAll(/{{dubhe-version}}/g, dubheVersion), 'utf-8');\n } else if (/\\.gitignore_$/.test(sourceFile)) {\n await fs.copyFile(sourceFile, destFile.replace(/_$/, ''));\n } else {\n await fs.copyFile(sourceFile, destFile);\n }\n }\n\n const cdProjectName = path.relative(cwd, destDir);\n\n const styles = {\n success: '\\x1b[32m%s\\x1b[0m',\n info: '\\x1b[36m%s\\x1b[0m',\n command: '\\x1b[33m%s\\x1b[0m',\n separator: '\\x1b[90m%s\\x1b[0m'\n };\n\n console.log('\\n' + '='.repeat(60));\n console.log(styles.success, '🎉 Project creation successful!');\n console.log(styles.info, `📁 Project location: ${destDir}`);\n console.log(styles.separator, '-'.repeat(60));\n console.log(styles.info, 'Next steps:\\n');\n\n if (destDir !== cwd) {\n console.log(\n styles.command,\n ` cd ${cdProjectName.includes(' ') ? `\"${cdProjectName}\"` : cdProjectName}`\n );\n }\n\n const actualTemplate = selectedTemplate.value;\n\n switch (actualTemplate) {\n case '101':\n case 'web':\n console.log(styles.command, ` ${pkgManager} install`);\n console.log(styles.command, ` ${pkgManager} dubhe doctor`);\n console.log(styles.command, ` ${pkgManager} run dev`);\n break;\n case 'contract':\n console.log(styles.command, ` ${pkgManager} install`);\n console.log(styles.command, ` ${pkgManager} dubhe doctor`);\n break;\n }\n\n console.log(styles.separator, '\\n' + '='.repeat(60) + '\\n');\n};\n\nfunction pkgFromUserAgent(userAgent: string | undefined) {\n if (!userAgent) return undefined;\n const pkgSpec = userAgent.split(' ')[0];\n const pkgSpecArr = pkgSpec.split('/');\n return {\n name: pkgSpecArr[0],\n version: pkgSpecArr[1]\n };\n}\n\ninit().catch((e) => {\n console.error(e);\n});\n","interface Template {\n title: string;\n description: string;\n value: string;\n path: string;\n}\n\ninterface Chain {\n title: string;\n description: string;\n value: string;\n supportedTemplates: Template[];\n}\n\nconst TEMPLATES = {\n QUICK_START: {\n title: '101',\n description: 'Quick start',\n value: '101',\n path: '101/{chain}-template'\n },\n WEB: {\n title: 'Web',\n description: 'Web template',\n value: 'web',\n path: 'nextjs/{chain}-template'\n },\n CONTRACT: {\n title: 'Contract',\n description: 'Contract template',\n value: 'contract',\n path: 'contract/{chain}-template'\n }\n} as const;\n\nexport const CHAINS: Chain[] = [\n {\n title: 'sui',\n description: 'Sui',\n value: 'sui',\n supportedTemplates: [TEMPLATES.QUICK_START, TEMPLATES.WEB, TEMPLATES.CONTRACT]\n },\n {\n title: 'aptos',\n description: 'Aptos',\n value: 'aptos',\n supportedTemplates: [TEMPLATES.QUICK_START, TEMPLATES.WEB, TEMPLATES.CONTRACT]\n },\n {\n title: 'rooch',\n description: 'Rooch',\n value: 'rooch',\n supportedTemplates: [TEMPLATES.QUICK_START]\n },\n {\n title: 'initia',\n description: 'Initia',\n value: 'initia',\n supportedTemplates: [TEMPLATES.QUICK_START]\n },\n {\n title: 'movement',\n description: 'Movement',\n value: 'movement',\n supportedTemplates: [TEMPLATES.QUICK_START]\n }\n];\n","{\n \"name\": \"create-dubhe\",\n \"version\": \"1.2.0-pre.124\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/0xobelisk/dubhe.git\"\n },\n \"license\": \"MIT\",\n \"author\": \"team@obelisk.build\",\n \"type\": \"module\",\n \"bin\": \"bin/cli.js\",\n \"files\": [\n \"bin\",\n \"dist\",\n \"templates\"\n ],\n \"scripts\": {\n \"build\": \"pnpm run type-check && pnpm run build:js\",\n \"build:js\": \"tsup && pnpm run copy-templates\",\n \"clean\": \"pnpm run clean:js\",\n \"clean:js\": \"shx rm -rf dist\",\n \"cocos-js-build\": \"cd cocos-lib-builder && npm i && browserify aptos.js -p esmify > ../templates/cocos/aptos-template/assets/lib/dubhe.js && browserify sui.js -p esmify > ../templates/cocos/sui-template/assets/lib/dubhe.js\",\n \"copy-templates\": \"tsx ./scripts/copy-templates.ts\",\n \"dev\": \"tsup --watch\",\n \"format\": \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n \"lint\": \"eslint . --ext .ts\",\n \"type-check\": \"tsc --noEmit\",\n \"validate\": \"pnpm format:check && pnpm lint && pnpm type-check\"\n },\n \"dependencies\": {\n \"browser-resolve\": \"^2.0.0\",\n \"browser-sync\": \"^2.29.3\",\n \"fast-glob\": \"^3.3.3\",\n \"yargs-interactive\": \"^3.0.1\"\n },\n \"devDependencies\": {\n \"@types/cross-spawn\": \"^6.0.2\",\n \"@types/minimist\": \"^1.2.2\",\n \"@types/prompts\": \"^2.4.3\",\n \"@types/yargs-interactive\": \"^2.1.6\",\n \"browserify\": \"^17.0.0\",\n \"cross-spawn\": \"^7.0.3\",\n \"eslint\": \"^9.0.0\",\n \"eslint-config-prettier\": \"^9.1.0\",\n \"esmify\": \"^2.1.1\",\n \"kolorist\": \"^1.7.0\",\n \"prettier\": \"3.3.3\",\n \"prompts\": \"^2.4.2\",\n \"typescript\": \"^5.8.3\"\n },\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","import fs from 'node:fs/promises';\n\nexport async function exists(path: string) {\n try {\n await fs.access(path);\n return true;\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["fs","path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../src/bin/cli.ts","../src/config/chains.ts","../package.json","../src/exists.ts"],"sourcesContent":["import { fileURLToPath } from 'node:url';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport glob from 'fast-glob';\nimport yargsInteractive from 'yargs-interactive';\nimport { CHAINS } from '../config/chains';\nimport packageJson from '../../package.json';\nimport { exists } from '../exists';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nconst cwd = process.cwd();\n\nconst init = async () => {\n // Prepare chain options\n const chainChoices = CHAINS.map(({ title, description, value }) => ({\n name: `${title} - ${description}`,\n value\n }));\n\n // Step 1: Choose project name and chain\n const firstStep = await yargsInteractive()\n .usage('$0 [args]')\n .interactive({\n interactive: { default: true },\n projectName: {\n describe: 'Name your project',\n type: 'input'\n },\n chain: {\n describe: 'Pick your chain',\n type: 'list',\n choices: chainChoices.map((c) => c.value)\n },\n dubheVersion: {\n describe: 'The version of Dubhe packages to use, defaults to latest',\n type: 'input',\n default: packageJson.version\n }\n });\n\n const { projectName, chain, dubheVersion } = firstStep;\n if (!projectName) throw new Error('No project name provided.');\n\n // Get available templates based on the selected chain\n const selectedChain = CHAINS.find((c) => c.value === chain);\n if (!selectedChain) {\n throw new Error('Invalid chain selection');\n }\n\n // Prepare platform options\n const platformChoices = selectedChain.supportedTemplates.map(({ title, description, value }) => ({\n name: `${title} - ${description}`,\n value\n }));\n\n // Step 2: Choose platform\n const secondStep = await yargsInteractive()\n .usage('$0 [args]')\n .interactive({\n interactive: { default: true },\n platform: {\n describe: 'Pick your platform',\n type: 'list',\n choices: platformChoices.map((c) => c.value)\n }\n });\n\n const { platform } = secondStep;\n\n const selectedTemplate = selectedChain.supportedTemplates.find((t) => t.value === platform);\n if (!selectedTemplate) {\n throw new Error('Invalid platform selection');\n }\n\n const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);\n const pkgManager = pkgInfo ? pkgInfo.name : 'npm';\n\n const sourceDir = path.join(\n __dirname,\n '..',\n 'templates',\n selectedTemplate.path.replace('{chain}', chain)\n );\n\n if (!(await exists(sourceDir))) {\n throw new Error(`Template directory not found: ${sourceDir}`);\n }\n\n const destDir = path.join(process.cwd(), projectName);\n if (await exists(destDir)) {\n throw new Error(`Target directory \"${destDir}\" already exists.`);\n }\n\n const files = await glob('**/*', { cwd: sourceDir, dot: true });\n\n for (const filename of files) {\n const sourceFile = path.join(sourceDir, filename);\n const destFile = path.join(destDir, filename);\n\n await fs.mkdir(path.dirname(destFile), { recursive: true });\n\n if (/package\\.json$/.test(sourceFile)) {\n const source = await fs.readFile(sourceFile, 'utf-8');\n await fs.writeFile(destFile, source.replaceAll(/{{dubhe-version}}/g, dubheVersion), 'utf-8');\n } else if (/\\.gitignore_$/.test(sourceFile)) {\n await fs.copyFile(sourceFile, destFile.replace(/_$/, ''));\n } else {\n await fs.copyFile(sourceFile, destFile);\n }\n }\n\n const cdProjectName = path.relative(cwd, destDir);\n\n const styles = {\n success: '\\x1b[32m%s\\x1b[0m',\n info: '\\x1b[36m%s\\x1b[0m',\n command: '\\x1b[33m%s\\x1b[0m',\n separator: '\\x1b[90m%s\\x1b[0m'\n };\n\n console.log('\\n' + '='.repeat(60));\n console.log(styles.success, '🎉 Project creation successful!');\n console.log(styles.info, `📁 Project location: ${destDir}`);\n console.log(styles.separator, '-'.repeat(60));\n console.log(styles.info, 'Next steps:\\n');\n\n if (destDir !== cwd) {\n console.log(\n styles.command,\n ` cd ${cdProjectName.includes(' ') ? `\"${cdProjectName}\"` : cdProjectName}`\n );\n }\n\n const actualTemplate = selectedTemplate.value;\n\n switch (actualTemplate) {\n case '101':\n case 'web':\n console.log(styles.command, ` ${pkgManager} install`);\n console.log(styles.command, ` ${pkgManager} dubhe doctor`);\n console.log(styles.command, ` ${pkgManager} run dev`);\n break;\n case 'contract':\n console.log(styles.command, ` ${pkgManager} install`);\n console.log(styles.command, ` ${pkgManager} dubhe doctor`);\n break;\n }\n\n console.log(styles.separator, '\\n' + '='.repeat(60) + '\\n');\n};\n\nfunction pkgFromUserAgent(userAgent: string | undefined) {\n if (!userAgent) return undefined;\n const pkgSpec = userAgent.split(' ')[0];\n const pkgSpecArr = pkgSpec.split('/');\n return {\n name: pkgSpecArr[0],\n version: pkgSpecArr[1]\n };\n}\n\ninit().catch((e) => {\n console.error(e);\n});\n","interface Template {\n title: string;\n description: string;\n value: string;\n path: string;\n}\n\ninterface Chain {\n title: string;\n description: string;\n value: string;\n supportedTemplates: Template[];\n}\n\nconst TEMPLATES = {\n QUICK_START: {\n title: '101',\n description: 'Quick start',\n value: '101',\n path: '101/{chain}-template'\n },\n WEB: {\n title: 'Web',\n description: 'Web template',\n value: 'web',\n path: 'nextjs/{chain}-template'\n },\n CONTRACT: {\n title: 'Contract',\n description: 'Contract template',\n value: 'contract',\n path: 'contract/{chain}-template'\n }\n} as const;\n\nexport const CHAINS: Chain[] = [\n {\n title: 'sui',\n description: 'Sui',\n value: 'sui',\n supportedTemplates: [TEMPLATES.QUICK_START, TEMPLATES.WEB, TEMPLATES.CONTRACT]\n },\n {\n title: 'aptos',\n description: 'Aptos',\n value: 'aptos',\n supportedTemplates: [TEMPLATES.QUICK_START, TEMPLATES.WEB, TEMPLATES.CONTRACT]\n },\n {\n title: 'rooch',\n description: 'Rooch',\n value: 'rooch',\n supportedTemplates: [TEMPLATES.QUICK_START]\n },\n {\n title: 'initia',\n description: 'Initia',\n value: 'initia',\n supportedTemplates: [TEMPLATES.QUICK_START]\n },\n {\n title: 'movement',\n description: 'Movement',\n value: 'movement',\n supportedTemplates: [TEMPLATES.QUICK_START]\n }\n];\n","{\n \"name\": \"create-dubhe\",\n \"version\": \"1.2.0-pre.125\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/0xobelisk/dubhe.git\"\n },\n \"license\": \"MIT\",\n \"author\": \"team@obelisk.build\",\n \"type\": \"module\",\n \"bin\": \"bin/cli.js\",\n \"files\": [\n \"bin\",\n \"dist\",\n \"templates\"\n ],\n \"scripts\": {\n \"build\": \"pnpm run type-check && pnpm run build:js\",\n \"build:js\": \"tsup && pnpm run copy-templates\",\n \"clean\": \"pnpm run clean:js\",\n \"clean:js\": \"shx rm -rf dist\",\n \"cocos-js-build\": \"cd cocos-lib-builder && npm i && browserify aptos.js -p esmify > ../templates/cocos/aptos-template/assets/lib/dubhe.js && browserify sui.js -p esmify > ../templates/cocos/sui-template/assets/lib/dubhe.js\",\n \"copy-templates\": \"tsx ./scripts/copy-templates.ts\",\n \"dev\": \"tsup --watch\",\n \"format\": \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n \"lint\": \"eslint . --ext .ts\",\n \"type-check\": \"tsc --noEmit\",\n \"validate\": \"pnpm format:check && pnpm lint && pnpm type-check\"\n },\n \"dependencies\": {\n \"browser-resolve\": \"^2.0.0\",\n \"browser-sync\": \"^2.29.3\",\n \"fast-glob\": \"^3.3.3\",\n \"yargs-interactive\": \"^3.0.1\"\n },\n \"devDependencies\": {\n \"@types/cross-spawn\": \"^6.0.2\",\n \"@types/minimist\": \"^1.2.2\",\n \"@types/prompts\": \"^2.4.3\",\n \"@types/yargs-interactive\": \"^2.1.6\",\n \"browserify\": \"^17.0.0\",\n \"cross-spawn\": \"^7.0.3\",\n \"eslint\": \"^9.0.0\",\n \"eslint-config-prettier\": \"^9.1.0\",\n \"esmify\": \"^2.1.1\",\n \"kolorist\": \"^1.7.0\",\n \"prettier\": \"3.3.3\",\n \"prompts\": \"^2.4.2\",\n \"typescript\": \"^5.8.3\"\n },\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","import fs from 'node:fs/promises';\n\nexport async function exists(path: string) {\n try {\n await fs.access(path);\n return true;\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["fs","path","fs"]}
|
package/package.json
CHANGED
|
@@ -39,9 +39,3 @@ source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-f
|
|
|
39
39
|
use_environment = "testnet"
|
|
40
40
|
manifest_digest = "7AFB66695545775FBFBB2D3078ADFD084244D5002392E837FDE21D9EA1C6D01C"
|
|
41
41
|
deps = { MoveStdlib = "MoveStdlib" }
|
|
42
|
-
|
|
43
|
-
[env.mainnet]
|
|
44
|
-
chain-id = "35834a8a"
|
|
45
|
-
original-published-id = "0x6cb54e8bbcf59cc976584330689073500af755b12d85bf4f56d7eea9f232b8aa"
|
|
46
|
-
latest-published-id = "0x6cb54e8bbcf59cc976584330689073500af755b12d85bf4f56d7eea9f232b8aa"
|
|
47
|
-
published-version = "1"
|
package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/dapp_system.move
CHANGED
|
@@ -278,9 +278,10 @@ public fun delete_field<DappKey: copy + drop>(
|
|
|
278
278
|
//
|
|
279
279
|
// Reactive writes allow one participant to modify another participant's UserStorage
|
|
280
280
|
// within a shared scene context. Four-layer security:
|
|
281
|
-
// 1. ctx.sender() must be from
|
|
282
|
-
//
|
|
283
|
-
//
|
|
281
|
+
// 1. ctx.sender() must be authorized to write `from` (canonical owner or its
|
|
282
|
+
// active session key — sessions are delegated proxies for in-scene actions)
|
|
283
|
+
// 2. from's canonical owner must be a registered scene participant
|
|
284
|
+
// 3. target's canonical owner must be a registered scene participant
|
|
284
285
|
// 4. scene must be active (not expired)
|
|
285
286
|
//
|
|
286
287
|
// Write fees are charged to the initiator (`from`) under the initiator-pays model.
|
|
@@ -304,8 +305,8 @@ public fun set_record_reactive<DappKey: copy + drop, PermType>(
|
|
|
304
305
|
let scene_id = dapp_service::scene_permit_id(permit);
|
|
305
306
|
let meta = dapp_service::scene_permit_meta(permit);
|
|
306
307
|
|
|
307
|
-
// 1. Sender must be
|
|
308
|
-
error::
|
|
308
|
+
// 1. Sender must be authorized to write `from` (canonical owner or active session key).
|
|
309
|
+
error::no_permission(dapp_service::is_write_authorized(from, ctx.sender(), ctx.epoch_timestamp_ms()));
|
|
309
310
|
// 2. Initiator must be a scene participant (O(1) DF lookup).
|
|
310
311
|
error::not_scene_participant(dapp_service::is_scene_participant(scene_id, dapp_service::canonical_owner(from)));
|
|
311
312
|
// 3. Target must be a scene participant (O(1) DF lookup).
|
|
@@ -343,7 +344,7 @@ public fun set_field_reactive<DappKey: copy + drop, PermType>(
|
|
|
343
344
|
let scene_id = dapp_service::scene_permit_id(permit);
|
|
344
345
|
let meta = dapp_service::scene_permit_meta(permit);
|
|
345
346
|
|
|
346
|
-
error::
|
|
347
|
+
error::no_permission(dapp_service::is_write_authorized(from, ctx.sender(), ctx.epoch_timestamp_ms()));
|
|
347
348
|
error::not_scene_participant(dapp_service::is_scene_participant(scene_id, dapp_service::canonical_owner(from)));
|
|
348
349
|
error::not_scene_participant(dapp_service::is_scene_participant(scene_id, dapp_service::canonical_owner(target)));
|
|
349
350
|
error::scene_expired(dapp_service::is_scene_active(meta, ctx.epoch_timestamp_ms()));
|
|
@@ -504,19 +505,27 @@ public fun remove_object_field<DappKey: copy + drop, ObjType, T: store + copy +
|
|
|
504
505
|
|
|
505
506
|
/// Remove and return a native-typed field from a permit-bound SceneStorage Bag.
|
|
506
507
|
/// Emits a Dubhe_Scene_DeleteField event so off-chain indexers stay in sync.
|
|
508
|
+
/// The caller identifies as `user_storage`'s canonical owner; the transaction may be
|
|
509
|
+
/// signed by the canonical owner or its active session key (is_write_authorized).
|
|
507
510
|
public fun remove_scene_field<DappKey: copy + drop, PermType, SceneType, T: store + copy + drop>(
|
|
508
|
-
_auth:
|
|
509
|
-
permit:
|
|
510
|
-
storage:
|
|
511
|
-
|
|
512
|
-
|
|
511
|
+
_auth: DappKey,
|
|
512
|
+
permit: &ScenePermit<PermType>,
|
|
513
|
+
storage: &mut SceneStorage<SceneType>,
|
|
514
|
+
user_storage: &UserStorage,
|
|
515
|
+
field_name: vector<u8>,
|
|
516
|
+
ctx: &TxContext,
|
|
513
517
|
): T {
|
|
514
518
|
let dapp_key_str = type_info::get_type_name_string<DappKey>();
|
|
515
519
|
error::dapp_key_mismatch(dapp_service::scene_storage_dapp_key(storage) == dapp_key_str);
|
|
516
520
|
error::dapp_key_mismatch(dapp_service::scene_permit_dapp_key(permit) == dapp_key_str);
|
|
521
|
+
error::dapp_key_mismatch(dapp_service::user_storage_dapp_key(user_storage) == dapp_key_str);
|
|
517
522
|
assert_scene_storage_bound_to_permit(permit, storage);
|
|
518
|
-
|
|
519
|
-
error::
|
|
523
|
+
let now_ms = ctx.epoch_timestamp_ms();
|
|
524
|
+
error::scene_expired(dapp_service::is_scene_active(dapp_service::scene_permit_meta(permit), now_ms));
|
|
525
|
+
error::no_permission(dapp_service::is_write_authorized(user_storage, ctx.sender(), now_ms));
|
|
526
|
+
error::not_scene_participant(dapp_service::is_participant_in_scene_permit(
|
|
527
|
+
permit, dapp_service::canonical_owner(user_storage)
|
|
528
|
+
));
|
|
520
529
|
|
|
521
530
|
let scene_type = *dapp_service::scene_storage_type(storage);
|
|
522
531
|
let scene_id = sui::object::uid_to_address(dapp_service::scene_storage_id(storage));
|
|
@@ -729,20 +738,28 @@ public fun share_scene_storage<DappKey: copy + drop, SceneType>(
|
|
|
729
738
|
}
|
|
730
739
|
|
|
731
740
|
/// Write a native-typed field into a permit-bound SceneStorage Bag and emit an event.
|
|
741
|
+
/// The caller identifies as `user_storage`'s canonical owner; the transaction may be
|
|
742
|
+
/// signed by the canonical owner or its active session key (is_write_authorized).
|
|
732
743
|
public fun set_scene_field<DappKey: copy + drop, PermType, SceneType, T: store + copy + drop>(
|
|
733
|
-
_auth:
|
|
734
|
-
permit:
|
|
735
|
-
storage:
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
744
|
+
_auth: DappKey,
|
|
745
|
+
permit: &ScenePermit<PermType>,
|
|
746
|
+
storage: &mut SceneStorage<SceneType>,
|
|
747
|
+
user_storage: &UserStorage,
|
|
748
|
+
field_name: vector<u8>,
|
|
749
|
+
value: T,
|
|
750
|
+
ctx: &TxContext,
|
|
739
751
|
) {
|
|
740
752
|
let dapp_key_str = type_info::get_type_name_string<DappKey>();
|
|
741
753
|
error::dapp_key_mismatch(dapp_service::scene_storage_dapp_key(storage) == dapp_key_str);
|
|
742
754
|
error::dapp_key_mismatch(dapp_service::scene_permit_dapp_key(permit) == dapp_key_str);
|
|
755
|
+
error::dapp_key_mismatch(dapp_service::user_storage_dapp_key(user_storage) == dapp_key_str);
|
|
743
756
|
assert_scene_storage_bound_to_permit(permit, storage);
|
|
744
|
-
|
|
745
|
-
error::
|
|
757
|
+
let now_ms = ctx.epoch_timestamp_ms();
|
|
758
|
+
error::scene_expired(dapp_service::is_scene_active(dapp_service::scene_permit_meta(permit), now_ms));
|
|
759
|
+
error::no_permission(dapp_service::is_write_authorized(user_storage, ctx.sender(), now_ms));
|
|
760
|
+
error::not_scene_participant(dapp_service::is_participant_in_scene_permit(
|
|
761
|
+
permit, dapp_service::canonical_owner(user_storage)
|
|
762
|
+
));
|
|
746
763
|
|
|
747
764
|
let event_bytes = sui::bcs::to_bytes(&value);
|
|
748
765
|
dapp_service::set_scene_field(storage, field_name, value);
|
|
@@ -829,19 +846,25 @@ public fun destroy_scene_permit<DappKey: copy + drop, PermType>(
|
|
|
829
846
|
}
|
|
830
847
|
|
|
831
848
|
/// Helper: accept a scene invitation for a ScenePermit-backed scene.
|
|
832
|
-
/// Moves
|
|
849
|
+
/// Moves the caller's canonical owner from the invitees list to confirmed
|
|
850
|
+
/// participants. The transaction may be signed by the canonical owner or its
|
|
851
|
+
/// active session key (is_write_authorized) — the registered identity is
|
|
852
|
+
/// always the canonical owner address.
|
|
833
853
|
/// Guards: scene must be active AND the invitation window must not have expired.
|
|
834
854
|
public fun accept_scene_permit_invitation<DappKey: copy + drop, PermType>(
|
|
835
|
-
_auth:
|
|
836
|
-
permit:
|
|
837
|
-
|
|
855
|
+
_auth: DappKey,
|
|
856
|
+
permit: &mut ScenePermit<PermType>,
|
|
857
|
+
user_storage: &UserStorage,
|
|
858
|
+
ctx: &TxContext,
|
|
838
859
|
) {
|
|
839
860
|
let dapp_key_str = type_info::get_type_name_string<DappKey>();
|
|
840
861
|
error::dapp_key_mismatch(dapp_service::scene_permit_dapp_key(permit) == dapp_key_str);
|
|
862
|
+
error::dapp_key_mismatch(dapp_service::user_storage_dapp_key(user_storage) == dapp_key_str);
|
|
841
863
|
|
|
842
864
|
// Check both permit activity and invitation window before mutating.
|
|
843
865
|
// The immutable borrow of `meta` is released at the end of this block.
|
|
844
866
|
let now_ms = ctx.epoch_timestamp_ms();
|
|
867
|
+
error::no_permission(dapp_service::is_write_authorized(user_storage, ctx.sender(), now_ms));
|
|
845
868
|
{
|
|
846
869
|
let meta = dapp_service::scene_permit_meta(permit);
|
|
847
870
|
error::scene_expired(dapp_service::is_scene_active(meta, now_ms));
|
|
@@ -851,54 +874,71 @@ public fun accept_scene_permit_invitation<DappKey: copy + drop, PermType>(
|
|
|
851
874
|
};
|
|
852
875
|
};
|
|
853
876
|
|
|
854
|
-
dapp_service::
|
|
877
|
+
let owner = dapp_service::canonical_owner(user_storage);
|
|
878
|
+
dapp_service::accept_invitation_in_scene_permit(permit, owner);
|
|
855
879
|
dubhe_events::emit_scene_permit_accept(
|
|
856
880
|
dapp_key_str,
|
|
857
881
|
*dapp_service::scene_permit_type(permit),
|
|
858
882
|
sui::object::uid_to_address(dapp_service::scene_permit_id(permit)),
|
|
859
|
-
|
|
883
|
+
owner,
|
|
860
884
|
);
|
|
861
885
|
}
|
|
862
886
|
|
|
863
|
-
/// Helper: add the caller as a confirmed participant in a
|
|
887
|
+
/// Helper: add the caller's canonical owner as a confirmed participant in a
|
|
888
|
+
/// ScenePermit. The transaction may be signed by the canonical owner or its
|
|
889
|
+
/// active session key (is_write_authorized) — the registered identity is
|
|
890
|
+
/// always the canonical owner address.
|
|
864
891
|
/// The permit must still be active — joining an expired permit is meaningless and
|
|
865
892
|
/// wastes gas on a DF write that can never be used for reactive writes.
|
|
866
893
|
public fun join_scene_permit<DappKey: copy + drop, PermType>(
|
|
867
|
-
_auth:
|
|
868
|
-
permit:
|
|
869
|
-
|
|
894
|
+
_auth: DappKey,
|
|
895
|
+
permit: &mut ScenePermit<PermType>,
|
|
896
|
+
user_storage: &UserStorage,
|
|
897
|
+
ctx: &TxContext,
|
|
870
898
|
) {
|
|
871
899
|
let dapp_key_str = type_info::get_type_name_string<DappKey>();
|
|
872
900
|
error::dapp_key_mismatch(dapp_service::scene_permit_dapp_key(permit) == dapp_key_str);
|
|
873
|
-
error::
|
|
874
|
-
let
|
|
875
|
-
dapp_service::
|
|
901
|
+
error::dapp_key_mismatch(dapp_service::user_storage_dapp_key(user_storage) == dapp_key_str);
|
|
902
|
+
let now_ms = ctx.epoch_timestamp_ms();
|
|
903
|
+
error::scene_expired(dapp_service::is_scene_active(dapp_service::scene_permit_meta(permit), now_ms));
|
|
904
|
+
error::no_permission(dapp_service::is_write_authorized(user_storage, ctx.sender(), now_ms));
|
|
905
|
+
let owner = dapp_service::canonical_owner(user_storage);
|
|
906
|
+
let was_participant = dapp_service::is_participant_in_scene_permit(permit, owner);
|
|
907
|
+
dapp_service::add_participant_in_scene_permit(permit, owner);
|
|
876
908
|
if (!was_participant) {
|
|
877
909
|
dubhe_events::emit_scene_permit_join(
|
|
878
910
|
dapp_key_str,
|
|
879
911
|
*dapp_service::scene_permit_type(permit),
|
|
880
912
|
sui::object::uid_to_address(dapp_service::scene_permit_id(permit)),
|
|
881
|
-
|
|
913
|
+
owner,
|
|
882
914
|
);
|
|
883
915
|
};
|
|
884
916
|
}
|
|
885
917
|
|
|
886
|
-
/// Helper: remove the caller from participants in a
|
|
918
|
+
/// Helper: remove the caller's canonical owner from participants in a
|
|
919
|
+
/// SceneStorage scene. The transaction may be signed by the canonical owner or
|
|
920
|
+
/// its active session key (is_write_authorized).
|
|
887
921
|
public fun leave_scene_permit<DappKey: copy + drop, PermType>(
|
|
888
|
-
_auth:
|
|
889
|
-
permit:
|
|
890
|
-
|
|
922
|
+
_auth: DappKey,
|
|
923
|
+
permit: &mut ScenePermit<PermType>,
|
|
924
|
+
user_storage: &UserStorage,
|
|
925
|
+
ctx: &TxContext,
|
|
891
926
|
) {
|
|
892
927
|
let dapp_key_str = type_info::get_type_name_string<DappKey>();
|
|
893
928
|
error::dapp_key_mismatch(dapp_service::scene_permit_dapp_key(permit) == dapp_key_str);
|
|
894
|
-
|
|
895
|
-
dapp_service::
|
|
929
|
+
error::dapp_key_mismatch(dapp_service::user_storage_dapp_key(user_storage) == dapp_key_str);
|
|
930
|
+
error::no_permission(dapp_service::is_write_authorized(
|
|
931
|
+
user_storage, ctx.sender(), ctx.epoch_timestamp_ms()
|
|
932
|
+
));
|
|
933
|
+
let owner = dapp_service::canonical_owner(user_storage);
|
|
934
|
+
let was_participant = dapp_service::is_participant_in_scene_permit(permit, owner);
|
|
935
|
+
dapp_service::remove_participant_in_scene_permit(permit, owner);
|
|
896
936
|
if (was_participant) {
|
|
897
937
|
dubhe_events::emit_scene_permit_leave(
|
|
898
938
|
dapp_key_str,
|
|
899
939
|
*dapp_service::scene_permit_type(permit),
|
|
900
940
|
sui::object::uid_to_address(dapp_service::scene_permit_id(permit)),
|
|
901
|
-
|
|
941
|
+
owner,
|
|
902
942
|
);
|
|
903
943
|
};
|
|
904
944
|
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/// Unit tests — Session keys acting in scenes
|
|
2
|
+
///
|
|
3
|
+
/// Sessions are delegated proxies: a transaction signed by an active session
|
|
4
|
+
/// key must be able to perform in-scene actions on behalf of its canonical
|
|
5
|
+
/// owner, while the on-chain identity (permit participants, scene events)
|
|
6
|
+
/// always resolves to the canonical owner address.
|
|
7
|
+
///
|
|
8
|
+
/// Covers:
|
|
9
|
+
/// reactive writes:
|
|
10
|
+
/// session key may initiate set_record_reactive / set_field_reactive
|
|
11
|
+
/// expired session is rejected
|
|
12
|
+
/// stranger (neither owner nor session) is rejected
|
|
13
|
+
/// permit participation:
|
|
14
|
+
/// join_scene_permit registers the canonical owner, not the session address
|
|
15
|
+
/// accept_scene_permit_invitation moves the canonical owner to participants
|
|
16
|
+
/// leave_scene_permit removes the canonical owner
|
|
17
|
+
/// unauthorized sender cannot join on someone else's behalf
|
|
18
|
+
/// scene field writes:
|
|
19
|
+
/// session key may set_scene_field / remove_scene_field
|
|
20
|
+
/// non-participant canonical owner is rejected
|
|
21
|
+
#[test_only]
|
|
22
|
+
module dubhe::scene_session_test;
|
|
23
|
+
|
|
24
|
+
use dubhe::dapp_service::{Self, UserStorage, ScenePermit};
|
|
25
|
+
use dubhe::dapp_system;
|
|
26
|
+
use sui::bcs::to_bytes;
|
|
27
|
+
use sui::tx_context;
|
|
28
|
+
|
|
29
|
+
public struct SceneSessionKey has copy, drop {}
|
|
30
|
+
|
|
31
|
+
const OWNER: address = @0xA1;
|
|
32
|
+
const SESSION: address = @0xA2;
|
|
33
|
+
const PEER: address = @0xB1;
|
|
34
|
+
const OTHER: address = @0xC1;
|
|
35
|
+
|
|
36
|
+
const NOW: u64 = 1_000;
|
|
37
|
+
const SESSION_EXPIRES: u64 = 10_000;
|
|
38
|
+
|
|
39
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
fun make_us(owner: address, ctx: &mut TxContext): UserStorage {
|
|
42
|
+
dapp_service::create_user_storage_for_testing<SceneSessionKey>(owner, ctx)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// UserStorage owned by `owner` with an active session key bound to SESSION.
|
|
46
|
+
fun make_us_with_session(owner: address, ctx: &mut TxContext): UserStorage {
|
|
47
|
+
let mut us = make_us(owner, ctx);
|
|
48
|
+
dapp_service::set_session_key_for_testing(&mut us, SESSION, SESSION_EXPIRES);
|
|
49
|
+
us
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fun make_permit(
|
|
53
|
+
participants: vector<address>,
|
|
54
|
+
ctx: &mut TxContext,
|
|
55
|
+
): ScenePermit<SceneSessionKey> {
|
|
56
|
+
dapp_service::create_scene_permit_for_testing<SceneSessionKey, SceneSessionKey>(
|
|
57
|
+
participants, std::option::none(), std::option::none(), ctx,
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// Transaction context whose sender is `sender` at epoch time NOW.
|
|
62
|
+
fun ctx_for(sender: address): TxContext {
|
|
63
|
+
tx_context::new_from_hint(sender, 0, 0, NOW, 0)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fun key_for(name: vector<u8>): vector<vector<u8>> { vector[name] }
|
|
67
|
+
|
|
68
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
69
|
+
// Reactive writes initiated by a session key
|
|
70
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
71
|
+
|
|
72
|
+
#[test]
|
|
73
|
+
fun test_session_can_initiate_reactive_write() {
|
|
74
|
+
let mut ctx = ctx_for(SESSION);
|
|
75
|
+
|
|
76
|
+
let permit = make_permit(vector[OWNER, PEER], &mut ctx);
|
|
77
|
+
let mut from = make_us_with_session(OWNER, &mut ctx);
|
|
78
|
+
let mut target = make_us(PEER, &mut ctx);
|
|
79
|
+
|
|
80
|
+
dapp_system::set_record_reactive<SceneSessionKey, SceneSessionKey>(
|
|
81
|
+
SceneSessionKey {}, &permit, &mut from, &mut target,
|
|
82
|
+
key_for(b"hp"), vector[b"v"], vector[to_bytes(&100u64)], &mut ctx,
|
|
83
|
+
);
|
|
84
|
+
dapp_system::set_field_reactive<SceneSessionKey, SceneSessionKey>(
|
|
85
|
+
SceneSessionKey {}, &permit, &mut from, &mut target,
|
|
86
|
+
key_for(b"hp"), b"v", to_bytes(&80u64), &mut ctx,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
assert!(dapp_service::has_user_record<SceneSessionKey>(&target, key_for(b"hp")), 0);
|
|
90
|
+
// Fees are charged to the initiator's storage, not the target's.
|
|
91
|
+
assert!(dapp_service::write_count(&from) == 2, 1);
|
|
92
|
+
assert!(dapp_service::write_count(&target) == 0, 2);
|
|
93
|
+
|
|
94
|
+
dapp_service::destroy_user_storage(from);
|
|
95
|
+
dapp_service::destroy_user_storage(target);
|
|
96
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
#[test]
|
|
100
|
+
#[expected_failure]
|
|
101
|
+
fun test_expired_session_reactive_aborts() {
|
|
102
|
+
// Session expires exactly at SESSION_EXPIRES — a transaction at that time is rejected.
|
|
103
|
+
let mut ctx = tx_context::new_from_hint(SESSION, 0, 0, SESSION_EXPIRES, 0);
|
|
104
|
+
|
|
105
|
+
let permit = make_permit(vector[OWNER, PEER], &mut ctx);
|
|
106
|
+
let mut from = make_us_with_session(OWNER, &mut ctx);
|
|
107
|
+
let mut target = make_us(PEER, &mut ctx);
|
|
108
|
+
|
|
109
|
+
dapp_system::set_record_reactive<SceneSessionKey, SceneSessionKey>(
|
|
110
|
+
SceneSessionKey {}, &permit, &mut from, &mut target,
|
|
111
|
+
key_for(b"hp"), vector[b"v"], vector[to_bytes(&100u64)], &mut ctx,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
dapp_service::destroy_user_storage(from);
|
|
115
|
+
dapp_service::destroy_user_storage(target);
|
|
116
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#[test]
|
|
120
|
+
#[expected_failure]
|
|
121
|
+
fun test_stranger_reactive_aborts() {
|
|
122
|
+
let mut ctx = ctx_for(OTHER);
|
|
123
|
+
|
|
124
|
+
let permit = make_permit(vector[OWNER, PEER], &mut ctx);
|
|
125
|
+
let mut from = make_us_with_session(OWNER, &mut ctx);
|
|
126
|
+
let mut target = make_us(PEER, &mut ctx);
|
|
127
|
+
|
|
128
|
+
dapp_system::set_record_reactive<SceneSessionKey, SceneSessionKey>(
|
|
129
|
+
SceneSessionKey {}, &permit, &mut from, &mut target,
|
|
130
|
+
key_for(b"hp"), vector[b"v"], vector[to_bytes(&100u64)], &mut ctx,
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
dapp_service::destroy_user_storage(from);
|
|
134
|
+
dapp_service::destroy_user_storage(target);
|
|
135
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
139
|
+
// Permit participation through a session key registers the canonical owner
|
|
140
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
141
|
+
|
|
142
|
+
#[test]
|
|
143
|
+
fun test_session_join_registers_canonical_owner() {
|
|
144
|
+
let mut ctx = ctx_for(SESSION);
|
|
145
|
+
|
|
146
|
+
let mut permit = make_permit(vector[], &mut ctx);
|
|
147
|
+
let us = make_us_with_session(OWNER, &mut ctx);
|
|
148
|
+
|
|
149
|
+
dapp_system::join_scene_permit<SceneSessionKey, SceneSessionKey>(
|
|
150
|
+
SceneSessionKey {}, &mut permit, &us, &ctx,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
assert!(dapp_service::is_participant_in_scene_permit(&permit, OWNER), 0);
|
|
154
|
+
assert!(!dapp_service::is_participant_in_scene_permit(&permit, SESSION), 1);
|
|
155
|
+
|
|
156
|
+
dapp_service::destroy_user_storage(us);
|
|
157
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
#[test]
|
|
161
|
+
fun test_session_accept_invitation_registers_canonical_owner() {
|
|
162
|
+
let mut ctx = ctx_for(SESSION);
|
|
163
|
+
|
|
164
|
+
let mut permit = dapp_service::create_scene_permit_with_invitations_for_testing<
|
|
165
|
+
SceneSessionKey, SceneSessionKey
|
|
166
|
+
>(vector[OWNER], std::option::none(), std::option::none(), &mut ctx);
|
|
167
|
+
let us = make_us_with_session(OWNER, &mut ctx);
|
|
168
|
+
|
|
169
|
+
dapp_system::accept_scene_permit_invitation<SceneSessionKey, SceneSessionKey>(
|
|
170
|
+
SceneSessionKey {}, &mut permit, &us, &ctx,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
assert!(dapp_service::is_participant_in_scene_permit(&permit, OWNER), 0);
|
|
174
|
+
assert!(!dapp_service::is_participant_in_scene_permit(&permit, SESSION), 1);
|
|
175
|
+
|
|
176
|
+
dapp_service::destroy_user_storage(us);
|
|
177
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#[test]
|
|
181
|
+
fun test_session_leave_removes_canonical_owner() {
|
|
182
|
+
let mut ctx = ctx_for(SESSION);
|
|
183
|
+
|
|
184
|
+
let mut permit = make_permit(vector[OWNER], &mut ctx);
|
|
185
|
+
let us = make_us_with_session(OWNER, &mut ctx);
|
|
186
|
+
|
|
187
|
+
dapp_system::leave_scene_permit<SceneSessionKey, SceneSessionKey>(
|
|
188
|
+
SceneSessionKey {}, &mut permit, &us, &ctx,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
assert!(!dapp_service::is_participant_in_scene_permit(&permit, OWNER), 0);
|
|
192
|
+
|
|
193
|
+
dapp_service::destroy_user_storage(us);
|
|
194
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
#[test]
|
|
198
|
+
#[expected_failure]
|
|
199
|
+
fun test_join_unauthorized_sender_aborts() {
|
|
200
|
+
// OTHER is neither the canonical owner nor the session key of `us`.
|
|
201
|
+
let mut ctx = ctx_for(OTHER);
|
|
202
|
+
|
|
203
|
+
let mut permit = make_permit(vector[], &mut ctx);
|
|
204
|
+
let us = make_us_with_session(OWNER, &mut ctx);
|
|
205
|
+
|
|
206
|
+
dapp_system::join_scene_permit<SceneSessionKey, SceneSessionKey>(
|
|
207
|
+
SceneSessionKey {}, &mut permit, &us, &ctx,
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
dapp_service::destroy_user_storage(us);
|
|
211
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
215
|
+
// Scene field writes through a session key
|
|
216
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
217
|
+
|
|
218
|
+
#[test]
|
|
219
|
+
fun test_session_set_and_remove_scene_field() {
|
|
220
|
+
let mut ctx = ctx_for(SESSION);
|
|
221
|
+
|
|
222
|
+
let ds = dapp_service::create_dapp_storage_for_testing<SceneSessionKey>(&mut ctx);
|
|
223
|
+
let permit = make_permit(vector[OWNER], &mut ctx);
|
|
224
|
+
let us = make_us_with_session(OWNER, &mut ctx);
|
|
225
|
+
|
|
226
|
+
let mut scene = dapp_system::new_typed_scene_with_permit<
|
|
227
|
+
SceneSessionKey, SceneSessionKey, SceneSessionKey
|
|
228
|
+
>(SceneSessionKey {}, &ds, &permit, b"battle", &mut ctx);
|
|
229
|
+
|
|
230
|
+
dapp_system::set_scene_field<SceneSessionKey, SceneSessionKey, SceneSessionKey, u64>(
|
|
231
|
+
SceneSessionKey {}, &permit, &mut scene, &us, b"hp", 100u64, &ctx,
|
|
232
|
+
);
|
|
233
|
+
assert!(dapp_system::get_scene_field<SceneSessionKey, u64>(&scene, b"hp") == 100, 0);
|
|
234
|
+
|
|
235
|
+
let removed: u64 = dapp_system::remove_scene_field<
|
|
236
|
+
SceneSessionKey, SceneSessionKey, SceneSessionKey, u64
|
|
237
|
+
>(SceneSessionKey {}, &permit, &mut scene, &us, b"hp", &ctx);
|
|
238
|
+
assert!(removed == 100, 1);
|
|
239
|
+
|
|
240
|
+
dapp_system::destroy_typed_scene<SceneSessionKey, SceneSessionKey>(SceneSessionKey {}, scene);
|
|
241
|
+
dapp_service::destroy_user_storage(us);
|
|
242
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
243
|
+
dapp_system::destroy_dapp_storage(ds);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
#[test]
|
|
247
|
+
#[expected_failure]
|
|
248
|
+
fun test_scene_field_non_participant_aborts() {
|
|
249
|
+
// `us` belongs to OTHER who never joined the permit — write must abort
|
|
250
|
+
// even though OTHER signed the transaction themselves.
|
|
251
|
+
let mut ctx = ctx_for(OTHER);
|
|
252
|
+
|
|
253
|
+
let ds = dapp_service::create_dapp_storage_for_testing<SceneSessionKey>(&mut ctx);
|
|
254
|
+
let permit = make_permit(vector[OWNER], &mut ctx);
|
|
255
|
+
let us = make_us(OTHER, &mut ctx);
|
|
256
|
+
|
|
257
|
+
let mut scene = dapp_system::new_typed_scene_with_permit<
|
|
258
|
+
SceneSessionKey, SceneSessionKey, SceneSessionKey
|
|
259
|
+
>(SceneSessionKey {}, &ds, &permit, b"battle", &mut ctx);
|
|
260
|
+
|
|
261
|
+
dapp_system::set_scene_field<SceneSessionKey, SceneSessionKey, SceneSessionKey, u64>(
|
|
262
|
+
SceneSessionKey {}, &permit, &mut scene, &us, b"hp", 100u64, &ctx,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
dapp_system::destroy_typed_scene<SceneSessionKey, SceneSessionKey>(SceneSessionKey {}, scene);
|
|
266
|
+
dapp_service::destroy_user_storage(us);
|
|
267
|
+
dapp_service::destroy_scene_permit_for_testing(permit);
|
|
268
|
+
dapp_system::destroy_dapp_storage(ds);
|
|
269
|
+
}
|