create-dubhe 1.2.0-pre.83 → 1.2.0-pre.85
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 +3 -3
- package/templates/101/sui-template/packages/contracts/src/dubhe/Move.toml +1 -1
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/genesis.move +1 -2
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/scripts/migrate.move +5 -1
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/address_system.move +296 -0
- package/templates/101/sui-template/packages/contracts/src/dubhe/sources/tests/address.move +55 -0
- package/templates/contract/sui-template/src/dubhe/Move.lock +3 -3
- package/templates/contract/sui-template/src/dubhe/Move.toml +1 -1
- package/templates/contract/sui-template/src/dubhe/sources/codegen/genesis.move +1 -2
- package/templates/contract/sui-template/src/dubhe/sources/scripts/migrate.move +5 -1
- package/templates/contract/sui-template/src/dubhe/sources/systems/address_system.move +296 -0
- package/templates/contract/sui-template/src/dubhe/sources/tests/address.move +55 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/Move.lock +3 -3
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/Move.toml +1 -1
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/genesis.move +1 -2
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/scripts/migrate.move +5 -1
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/systems/address_system.move +296 -0
- package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/tests/address.move +55 -0
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.83\",\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.85\",\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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[move]
|
|
4
4
|
version = 3
|
|
5
|
-
manifest_digest = "
|
|
5
|
+
manifest_digest = "73896F00312A2E2C47F56DCCABF824967268FF718658DD5D0FFFE2E60824F94A"
|
|
6
6
|
deps_digest = "F8BBB0CCB2491CA29A3DF03D6F92277A4F3574266507ACD77214D37ECA3F3082"
|
|
7
7
|
dependencies = [
|
|
8
8
|
{ id = "Sui", name = "Sui" },
|
|
@@ -28,5 +28,5 @@ flavor = "sui"
|
|
|
28
28
|
[env.testnet]
|
|
29
29
|
chain-id = "4c78adac"
|
|
30
30
|
original-published-id = "0xa09cd4137e604ec5a7a88f72c572ecd064b0e713a3fbf705a88456cdbccf36c0"
|
|
31
|
-
latest-published-id = "
|
|
32
|
-
published-version = "
|
|
31
|
+
latest-published-id = "0x4613b8a390298cc450fe11360fddf41ba1bde38060163c5ca9f192a25b4f9f2e"
|
|
32
|
+
published-version = "3"
|
|
@@ -4,7 +4,7 @@ edition = "2024"
|
|
|
4
4
|
version = "1.0.0"
|
|
5
5
|
license = "apache2.0"
|
|
6
6
|
authors = ["Obelisk Labs"]
|
|
7
|
-
published-at = "
|
|
7
|
+
published-at = "0x4613b8a390298cc450fe11360fddf41ba1bde38060163c5ca9f192a25b4f9f2e"
|
|
8
8
|
|
|
9
9
|
[dependencies]
|
|
10
10
|
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "mainnet-v1.46.3" }
|
package/templates/101/sui-template/packages/contracts/src/dubhe/sources/codegen/genesis.move
CHANGED
|
@@ -82,8 +82,7 @@
|
|
|
82
82
|
dapp_system::upgrade_dapp(dapp_hub, dapp_key, new_package_id, new_version, ctx);
|
|
83
83
|
// Register new tables
|
|
84
84
|
// ==========================================
|
|
85
|
-
|
|
86
|
-
asset_remove_liquidity::register_table(dapp_hub, ctx);
|
|
85
|
+
|
|
87
86
|
// ==========================================
|
|
88
87
|
}
|
|
89
88
|
}
|
package/templates/101/sui-template/packages/contracts/src/dubhe/sources/scripts/migrate.move
CHANGED
|
@@ -2,7 +2,7 @@ module dubhe::migrate {
|
|
|
2
2
|
use dubhe::dapp_service::DappHub;
|
|
3
3
|
use dubhe::genesis;
|
|
4
4
|
|
|
5
|
-
const ON_CHAIN_VERSION: u32 =
|
|
5
|
+
const ON_CHAIN_VERSION: u32 = 3;
|
|
6
6
|
|
|
7
7
|
public fun on_chain_version(): u32 {
|
|
8
8
|
ON_CHAIN_VERSION
|
|
@@ -11,4 +11,8 @@ module dubhe::migrate {
|
|
|
11
11
|
public entry fun migrate_to_v2(dapp_hub: &mut DappHub, new_package_id: address, new_version: u32, ctx: &mut TxContext) {
|
|
12
12
|
genesis::upgrade(dapp_hub, new_package_id, new_version, ctx);
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
public entry fun migrate_to_v3(dapp_hub: &mut DappHub, new_package_id: address, new_version: u32, ctx: &mut TxContext) {
|
|
16
|
+
genesis::upgrade(dapp_hub, new_package_id, new_version, ctx);
|
|
17
|
+
}
|
|
14
18
|
}
|
package/templates/101/sui-template/packages/contracts/src/dubhe/sources/systems/address_system.move
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
module dubhe::address_system;
|
|
2
|
+
|
|
3
|
+
use std::ascii::String;
|
|
4
|
+
use sui::address;
|
|
5
|
+
use sui::hex;
|
|
6
|
+
|
|
7
|
+
#[test_only]
|
|
8
|
+
use sui::test_scenario;
|
|
9
|
+
|
|
10
|
+
// TX_HASH signature markers
|
|
11
|
+
const DUBHE_PREFIX: u8 = 0xDB;
|
|
12
|
+
const DUBHE_VERSION: u8 = 0x01;
|
|
13
|
+
const CHAIN_TYPE_EVM: u8 = 0xE1;
|
|
14
|
+
const CHAIN_TYPE_SOLANA: u8 = 0xE2;
|
|
15
|
+
|
|
16
|
+
// Constants
|
|
17
|
+
const TX_HASH_LENGTH: u64 = 32;
|
|
18
|
+
const SOLANA_ADDRESS_LENGTH: u64 = 32;
|
|
19
|
+
const EVM_ADDRESS_LENGTH: u64 = 20;
|
|
20
|
+
|
|
21
|
+
// Error codes
|
|
22
|
+
const E_INVALID_EVM_ADDRESS: u64 = 1;
|
|
23
|
+
const E_INVALID_SOLANA_ADDRESS: u64 = 2;
|
|
24
|
+
|
|
25
|
+
/// Detect chain type from tx_hash signature
|
|
26
|
+
/// Returns: 0 = SUI, 1 = EVM, 2 = Solana
|
|
27
|
+
/// Format: [0xDB][0xDB][0x01][CHAIN_TYPE][...28 bytes...]
|
|
28
|
+
fun detect_chain_type_from_tx_hash(tx_hash: &vector<u8>): u8 {
|
|
29
|
+
if (tx_hash.length() != TX_HASH_LENGTH ||
|
|
30
|
+
tx_hash[0] != DUBHE_PREFIX ||
|
|
31
|
+
tx_hash[1] != DUBHE_PREFIX ||
|
|
32
|
+
tx_hash[2] != DUBHE_VERSION) {
|
|
33
|
+
return 0
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let chain_type = tx_hash[3];
|
|
37
|
+
if (chain_type == CHAIN_TYPE_EVM) {
|
|
38
|
+
1
|
|
39
|
+
} else if (chain_type == CHAIN_TYPE_SOLANA) {
|
|
40
|
+
2
|
|
41
|
+
} else {
|
|
42
|
+
0
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fun hex_string_to_bytes(hex_str: String): vector<u8> {
|
|
47
|
+
let bytes = hex_str.into_bytes();
|
|
48
|
+
let hex_bytes = if (bytes.length() >= 2 && bytes[0] == 48 && (bytes[1] == 120 || bytes[1] == 88)) {
|
|
49
|
+
let mut result = vector[];
|
|
50
|
+
let mut i = 2;
|
|
51
|
+
while (i < bytes.length()) {
|
|
52
|
+
result.push_back(bytes[i]);
|
|
53
|
+
i = i + 1;
|
|
54
|
+
};
|
|
55
|
+
result
|
|
56
|
+
} else {
|
|
57
|
+
bytes
|
|
58
|
+
};
|
|
59
|
+
hex::decode(hex_bytes)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fun base58_decode(input: String): vector<u8> {
|
|
63
|
+
let base58_alphabet = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
64
|
+
let input_bytes = input.as_bytes();
|
|
65
|
+
let len = input_bytes.length();
|
|
66
|
+
|
|
67
|
+
let mut result: vector<u8> = vector[];
|
|
68
|
+
let mut j = 0;
|
|
69
|
+
while (j < SOLANA_ADDRESS_LENGTH) {
|
|
70
|
+
result.push_back(0u8);
|
|
71
|
+
j = j + 1;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
let mut i = 0;
|
|
75
|
+
while (i < len) {
|
|
76
|
+
let c = input_bytes[i];
|
|
77
|
+
let mut char_value: u64 = 0;
|
|
78
|
+
let mut found = false;
|
|
79
|
+
let mut k = 0;
|
|
80
|
+
while (k < 58) {
|
|
81
|
+
if (base58_alphabet[k] == c) {
|
|
82
|
+
char_value = k;
|
|
83
|
+
found = true;
|
|
84
|
+
break
|
|
85
|
+
};
|
|
86
|
+
k = k + 1;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
assert!(found, E_INVALID_SOLANA_ADDRESS);
|
|
90
|
+
|
|
91
|
+
let mut carry = char_value;
|
|
92
|
+
let mut m = (SOLANA_ADDRESS_LENGTH - 1);
|
|
93
|
+
while (m < SOLANA_ADDRESS_LENGTH) {
|
|
94
|
+
let byte_ref = vector::borrow_mut(&mut result, m);
|
|
95
|
+
let tmp = (*byte_ref as u64) * 58 + carry;
|
|
96
|
+
carry = tmp / 256;
|
|
97
|
+
*byte_ref = ((tmp % 256) as u8);
|
|
98
|
+
if (m == 0) break;
|
|
99
|
+
m = m - 1;
|
|
100
|
+
};
|
|
101
|
+
i = i + 1;
|
|
102
|
+
};
|
|
103
|
+
result
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// Convert EVM address to SUI address
|
|
107
|
+
/// Format: [12 zero bytes][20 bytes EVM address]
|
|
108
|
+
public fun evm_to_sui(evm_address_str: String): address {
|
|
109
|
+
let evm_bytes = hex_string_to_bytes(evm_address_str);
|
|
110
|
+
assert!(evm_bytes.length() == EVM_ADDRESS_LENGTH, E_INVALID_EVM_ADDRESS);
|
|
111
|
+
|
|
112
|
+
let mut sui_bytes = vector[];
|
|
113
|
+
let mut i = 0;
|
|
114
|
+
while (i < 12) {
|
|
115
|
+
sui_bytes.push_back(0u8);
|
|
116
|
+
i = i + 1;
|
|
117
|
+
};
|
|
118
|
+
sui_bytes.append(evm_bytes);
|
|
119
|
+
address::from_bytes(sui_bytes)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/// Convert Solana address to SUI address
|
|
123
|
+
/// Direct use of 32 bytes from Base58 decode
|
|
124
|
+
public fun solana_to_sui(solana_address_str: String): address {
|
|
125
|
+
let solana_bytes = base58_decode(solana_address_str);
|
|
126
|
+
assert!(solana_bytes.length() == SOLANA_ADDRESS_LENGTH, E_INVALID_SOLANA_ADDRESS);
|
|
127
|
+
address::from_bytes(solana_bytes)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fun base58_encode(input: vector<u8>): String {
|
|
131
|
+
let base58_alphabet = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
132
|
+
let mut result = vector::empty<u8>();
|
|
133
|
+
let mut num = input;
|
|
134
|
+
|
|
135
|
+
let mut leading_zeros = 0;
|
|
136
|
+
let mut i = 0;
|
|
137
|
+
while (i < num.length()) {
|
|
138
|
+
if (num[i] == 0) {
|
|
139
|
+
leading_zeros = leading_zeros + 1;
|
|
140
|
+
i = i + 1;
|
|
141
|
+
} else {
|
|
142
|
+
break
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
while (!is_zero(&num)) {
|
|
147
|
+
let remainder = div_mod_58(&mut num);
|
|
148
|
+
result.push_back(base58_alphabet[remainder]);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
let mut j = 0;
|
|
152
|
+
while (j < leading_zeros) {
|
|
153
|
+
result.push_back(49);
|
|
154
|
+
j = j + 1;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
vector::reverse(&mut result);
|
|
158
|
+
result.to_ascii_string()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
fun is_zero(num: &vector<u8>): bool {
|
|
162
|
+
let mut i = 0;
|
|
163
|
+
while (i < num.length()) {
|
|
164
|
+
if (num[i] != 0) {
|
|
165
|
+
return false
|
|
166
|
+
};
|
|
167
|
+
i = i + 1;
|
|
168
|
+
};
|
|
169
|
+
true
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
fun div_mod_58(num: &mut vector<u8>): u64 {
|
|
173
|
+
let mut remainder: u64 = 0;
|
|
174
|
+
let mut i = 0;
|
|
175
|
+
while (i < num.length()) {
|
|
176
|
+
let byte_ref = vector::borrow_mut(num, i);
|
|
177
|
+
let current = (remainder * 256) + (*byte_ref as u64);
|
|
178
|
+
*byte_ref = ((current / 58) as u8);
|
|
179
|
+
remainder = current % 58;
|
|
180
|
+
i = i + 1;
|
|
181
|
+
};
|
|
182
|
+
remainder
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// Get original address format based on tx_hash detection
|
|
186
|
+
/// Returns: EVM (0x...), Solana (Base58), or SUI (0x...) format
|
|
187
|
+
public fun ensure_origin(ctx: &TxContext): String {
|
|
188
|
+
let sui_address = ctx.sender();
|
|
189
|
+
let address_bytes = address::to_bytes(sui_address);
|
|
190
|
+
let tx_hash = ctx.digest();
|
|
191
|
+
let chain_type = detect_chain_type_from_tx_hash(tx_hash);
|
|
192
|
+
|
|
193
|
+
if (chain_type == 1) {
|
|
194
|
+
let mut evm_bytes = vector[];
|
|
195
|
+
let mut j = 12;
|
|
196
|
+
while (j < TX_HASH_LENGTH) {
|
|
197
|
+
evm_bytes.push_back(address_bytes[j]);
|
|
198
|
+
j = j + 1;
|
|
199
|
+
};
|
|
200
|
+
let hex_bytes = hex::encode(evm_bytes);
|
|
201
|
+
let mut result_bytes = b"0x";
|
|
202
|
+
result_bytes.append(hex_bytes);
|
|
203
|
+
result_bytes.to_ascii_string()
|
|
204
|
+
} else if (chain_type == 2) {
|
|
205
|
+
base58_encode(address_bytes)
|
|
206
|
+
} else {
|
|
207
|
+
let hex_bytes = hex::encode(address_bytes);
|
|
208
|
+
let mut result_bytes = b"0x";
|
|
209
|
+
result_bytes.append(hex_bytes);
|
|
210
|
+
result_bytes.to_ascii_string()
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// Check if transaction is from EVM chain
|
|
215
|
+
public fun is_evm_address(ctx: &TxContext): bool {
|
|
216
|
+
let tx_hash = ctx.digest();
|
|
217
|
+
detect_chain_type_from_tx_hash(tx_hash) == 1
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// Check if transaction is from Solana
|
|
221
|
+
public fun is_solana_address(ctx: &TxContext): bool {
|
|
222
|
+
let tx_hash = ctx.digest();
|
|
223
|
+
detect_chain_type_from_tx_hash(tx_hash) == 2
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/// Check if transaction is native SUI
|
|
227
|
+
public fun is_sui_address(ctx: &TxContext): bool {
|
|
228
|
+
let tx_hash = ctx.digest();
|
|
229
|
+
detect_chain_type_from_tx_hash(tx_hash) == 0
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ========== Test Utilities ==========
|
|
233
|
+
|
|
234
|
+
#[test_only]
|
|
235
|
+
/// Setup test scenario with EVM context
|
|
236
|
+
/// Input: EVM address as byte string, e.g., b"0x9168765EE952de7C6f8fC6FaD5Ec209B960b7622"
|
|
237
|
+
/// Format: [0xDB][0xDB][0x01][0xE1][...28 bytes...]
|
|
238
|
+
public fun setup_evm_scenario(scenario: &mut test_scenario::Scenario, evm_address_bytes: vector<u8>) {
|
|
239
|
+
use std::ascii;
|
|
240
|
+
|
|
241
|
+
// Convert EVM address string to SUI address
|
|
242
|
+
let evm_address_str = ascii::string(evm_address_bytes);
|
|
243
|
+
let sender = evm_to_sui(evm_address_str);
|
|
244
|
+
|
|
245
|
+
// Generate EVM tx_hash with Dubhe prefix at the beginning
|
|
246
|
+
let mut tx_hash = vector::empty<u8>();
|
|
247
|
+
|
|
248
|
+
// Mark as EVM with Dubhe prefix, version, and chain type
|
|
249
|
+
tx_hash.push_back(DUBHE_PREFIX); // 0xDB
|
|
250
|
+
tx_hash.push_back(DUBHE_PREFIX); // 0xDB
|
|
251
|
+
tx_hash.push_back(DUBHE_VERSION); // 0x01
|
|
252
|
+
tx_hash.push_back(CHAIN_TYPE_EVM); // 0xE1
|
|
253
|
+
|
|
254
|
+
// Fill remaining 28 bytes
|
|
255
|
+
let mut i = 0;
|
|
256
|
+
while (i < 28) {
|
|
257
|
+
tx_hash.push_back((i as u8));
|
|
258
|
+
i = i + 1;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// Set context
|
|
262
|
+
let ctx = test_scenario::ctx(scenario);
|
|
263
|
+
*ctx = tx_context::new(sender, tx_hash, 0, 0, 0);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
#[test_only]
|
|
267
|
+
/// Setup test scenario with Solana context
|
|
268
|
+
/// Input: Solana address as byte string, e.g., b"3vy8k1NAc3Q9EPvqrAuS4DG4qwbgVqfxznEdtcrL743L"
|
|
269
|
+
/// Format: [0xDB][0xDB][0x01][0xE2][...28 bytes...]
|
|
270
|
+
public fun setup_solana_scenario(scenario: &mut test_scenario::Scenario, solana_address_bytes: vector<u8>) {
|
|
271
|
+
use std::ascii;
|
|
272
|
+
|
|
273
|
+
// Convert Solana address string to SUI address
|
|
274
|
+
let solana_address_str = ascii::string(solana_address_bytes);
|
|
275
|
+
let sender = solana_to_sui(solana_address_str);
|
|
276
|
+
|
|
277
|
+
// Generate Solana tx_hash with Dubhe prefix at the beginning
|
|
278
|
+
let mut tx_hash = vector::empty<u8>();
|
|
279
|
+
|
|
280
|
+
// Mark as Solana with Dubhe prefix, version, and chain type
|
|
281
|
+
tx_hash.push_back(DUBHE_PREFIX); // 0xDB
|
|
282
|
+
tx_hash.push_back(DUBHE_PREFIX); // 0xDB
|
|
283
|
+
tx_hash.push_back(DUBHE_VERSION); // 0x01
|
|
284
|
+
tx_hash.push_back(CHAIN_TYPE_SOLANA); // 0xE2
|
|
285
|
+
|
|
286
|
+
// Fill remaining 28 bytes
|
|
287
|
+
let mut i = 0;
|
|
288
|
+
while (i < 28) {
|
|
289
|
+
tx_hash.push_back(((i + 100) as u8));
|
|
290
|
+
i = i + 1;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// Set context
|
|
294
|
+
let ctx = test_scenario::ctx(scenario);
|
|
295
|
+
*ctx = tx_context::new(sender, tx_hash, 0, 0, 0);
|
|
296
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#[test_only]
|
|
2
|
+
module dubhe::address_test;
|
|
3
|
+
|
|
4
|
+
use dubhe::address_system;
|
|
5
|
+
use sui::test_scenario;
|
|
6
|
+
use std::ascii::string;
|
|
7
|
+
|
|
8
|
+
#[test]
|
|
9
|
+
public fun test_address_conversion() {
|
|
10
|
+
let sui_sender = @0x1462cab50fe5998f8161378e5265f7920bfd9fbce604d602619962f608837217;
|
|
11
|
+
let sui_origin_string = string(b"0x1462cab50fe5998f8161378e5265f7920bfd9fbce604d602619962f608837217");
|
|
12
|
+
let evm_origin_string = string(b"0x9168765ee952de7c6f8fc6fad5ec209b960b7622");
|
|
13
|
+
let solana_origin_string = string(b"3vy8k1NAc3Q9EPvqrAuS4DG4qwbgVqfxznEdtcrL743L");
|
|
14
|
+
let mut scenario = test_scenario::begin(sui_sender);
|
|
15
|
+
|
|
16
|
+
// Test EVM address conversion
|
|
17
|
+
std::debug::print(&string(b"EVM address:"));
|
|
18
|
+
std::debug::print(&evm_origin_string);
|
|
19
|
+
let evm_sui_address = address_system::evm_to_sui(evm_origin_string);
|
|
20
|
+
std::debug::print(&string(b"EVM->SUI:"));
|
|
21
|
+
std::debug::print(&evm_sui_address.to_ascii_string());
|
|
22
|
+
|
|
23
|
+
// Test Solana address conversion
|
|
24
|
+
let solana_address_str = string(b"3vy8k1NAc3Q9EPvqrAuS4DG4qwbgVqfxznEdtcrL743L");
|
|
25
|
+
let solana_sui_address = address_system::solana_to_sui(solana_address_str);
|
|
26
|
+
std::debug::print(&string(b"Solana->SUI:"));
|
|
27
|
+
std::debug::print(&solana_sui_address.to_ascii_string());
|
|
28
|
+
|
|
29
|
+
// Test SUI address detection
|
|
30
|
+
{
|
|
31
|
+
let ctx = test_scenario::ctx(&mut scenario);
|
|
32
|
+
assert!(address_system::is_sui_address(ctx));
|
|
33
|
+
assert!(address_system::ensure_origin(ctx) == sui_origin_string);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Test EVM address detection
|
|
37
|
+
address_system::setup_evm_scenario(&mut scenario, b"0x9168765EE952de7C6f8fC6FaD5Ec209B960b7622");
|
|
38
|
+
{
|
|
39
|
+
let ctx = test_scenario::ctx(&mut scenario);
|
|
40
|
+
assert!(address_system::is_evm_address(ctx));
|
|
41
|
+
std::debug::print(&string(b"evm_origin_string:"));
|
|
42
|
+
std::debug::print(&address_system::ensure_origin(ctx));
|
|
43
|
+
assert!(address_system::ensure_origin(ctx) == evm_origin_string);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Test Solana address detection
|
|
47
|
+
address_system::setup_solana_scenario(&mut scenario, b"3vy8k1NAc3Q9EPvqrAuS4DG4qwbgVqfxznEdtcrL743L");
|
|
48
|
+
{
|
|
49
|
+
let ctx = test_scenario::ctx(&mut scenario);
|
|
50
|
+
assert!(address_system::is_solana_address(ctx));
|
|
51
|
+
assert!(address_system::ensure_origin(ctx) == solana_origin_string);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
scenario.end();
|
|
55
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[move]
|
|
4
4
|
version = 3
|
|
5
|
-
manifest_digest = "
|
|
5
|
+
manifest_digest = "73896F00312A2E2C47F56DCCABF824967268FF718658DD5D0FFFE2E60824F94A"
|
|
6
6
|
deps_digest = "F8BBB0CCB2491CA29A3DF03D6F92277A4F3574266507ACD77214D37ECA3F3082"
|
|
7
7
|
dependencies = [
|
|
8
8
|
{ id = "Sui", name = "Sui" },
|
|
@@ -28,5 +28,5 @@ flavor = "sui"
|
|
|
28
28
|
[env.testnet]
|
|
29
29
|
chain-id = "4c78adac"
|
|
30
30
|
original-published-id = "0xa09cd4137e604ec5a7a88f72c572ecd064b0e713a3fbf705a88456cdbccf36c0"
|
|
31
|
-
latest-published-id = "
|
|
32
|
-
published-version = "
|
|
31
|
+
latest-published-id = "0x4613b8a390298cc450fe11360fddf41ba1bde38060163c5ca9f192a25b4f9f2e"
|
|
32
|
+
published-version = "3"
|
|
@@ -4,7 +4,7 @@ edition = "2024"
|
|
|
4
4
|
version = "1.0.0"
|
|
5
5
|
license = "apache2.0"
|
|
6
6
|
authors = ["Obelisk Labs"]
|
|
7
|
-
published-at = "
|
|
7
|
+
published-at = "0x4613b8a390298cc450fe11360fddf41ba1bde38060163c5ca9f192a25b4f9f2e"
|
|
8
8
|
|
|
9
9
|
[dependencies]
|
|
10
10
|
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "mainnet-v1.46.3" }
|
|
@@ -82,8 +82,7 @@
|
|
|
82
82
|
dapp_system::upgrade_dapp(dapp_hub, dapp_key, new_package_id, new_version, ctx);
|
|
83
83
|
// Register new tables
|
|
84
84
|
// ==========================================
|
|
85
|
-
|
|
86
|
-
asset_remove_liquidity::register_table(dapp_hub, ctx);
|
|
85
|
+
|
|
87
86
|
// ==========================================
|
|
88
87
|
}
|
|
89
88
|
}
|
|
@@ -2,7 +2,7 @@ module dubhe::migrate {
|
|
|
2
2
|
use dubhe::dapp_service::DappHub;
|
|
3
3
|
use dubhe::genesis;
|
|
4
4
|
|
|
5
|
-
const ON_CHAIN_VERSION: u32 =
|
|
5
|
+
const ON_CHAIN_VERSION: u32 = 3;
|
|
6
6
|
|
|
7
7
|
public fun on_chain_version(): u32 {
|
|
8
8
|
ON_CHAIN_VERSION
|
|
@@ -11,4 +11,8 @@ module dubhe::migrate {
|
|
|
11
11
|
public entry fun migrate_to_v2(dapp_hub: &mut DappHub, new_package_id: address, new_version: u32, ctx: &mut TxContext) {
|
|
12
12
|
genesis::upgrade(dapp_hub, new_package_id, new_version, ctx);
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
public entry fun migrate_to_v3(dapp_hub: &mut DappHub, new_package_id: address, new_version: u32, ctx: &mut TxContext) {
|
|
16
|
+
genesis::upgrade(dapp_hub, new_package_id, new_version, ctx);
|
|
17
|
+
}
|
|
14
18
|
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
module dubhe::address_system;
|
|
2
|
+
|
|
3
|
+
use std::ascii::String;
|
|
4
|
+
use sui::address;
|
|
5
|
+
use sui::hex;
|
|
6
|
+
|
|
7
|
+
#[test_only]
|
|
8
|
+
use sui::test_scenario;
|
|
9
|
+
|
|
10
|
+
// TX_HASH signature markers
|
|
11
|
+
const DUBHE_PREFIX: u8 = 0xDB;
|
|
12
|
+
const DUBHE_VERSION: u8 = 0x01;
|
|
13
|
+
const CHAIN_TYPE_EVM: u8 = 0xE1;
|
|
14
|
+
const CHAIN_TYPE_SOLANA: u8 = 0xE2;
|
|
15
|
+
|
|
16
|
+
// Constants
|
|
17
|
+
const TX_HASH_LENGTH: u64 = 32;
|
|
18
|
+
const SOLANA_ADDRESS_LENGTH: u64 = 32;
|
|
19
|
+
const EVM_ADDRESS_LENGTH: u64 = 20;
|
|
20
|
+
|
|
21
|
+
// Error codes
|
|
22
|
+
const E_INVALID_EVM_ADDRESS: u64 = 1;
|
|
23
|
+
const E_INVALID_SOLANA_ADDRESS: u64 = 2;
|
|
24
|
+
|
|
25
|
+
/// Detect chain type from tx_hash signature
|
|
26
|
+
/// Returns: 0 = SUI, 1 = EVM, 2 = Solana
|
|
27
|
+
/// Format: [0xDB][0xDB][0x01][CHAIN_TYPE][...28 bytes...]
|
|
28
|
+
fun detect_chain_type_from_tx_hash(tx_hash: &vector<u8>): u8 {
|
|
29
|
+
if (tx_hash.length() != TX_HASH_LENGTH ||
|
|
30
|
+
tx_hash[0] != DUBHE_PREFIX ||
|
|
31
|
+
tx_hash[1] != DUBHE_PREFIX ||
|
|
32
|
+
tx_hash[2] != DUBHE_VERSION) {
|
|
33
|
+
return 0
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let chain_type = tx_hash[3];
|
|
37
|
+
if (chain_type == CHAIN_TYPE_EVM) {
|
|
38
|
+
1
|
|
39
|
+
} else if (chain_type == CHAIN_TYPE_SOLANA) {
|
|
40
|
+
2
|
|
41
|
+
} else {
|
|
42
|
+
0
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fun hex_string_to_bytes(hex_str: String): vector<u8> {
|
|
47
|
+
let bytes = hex_str.into_bytes();
|
|
48
|
+
let hex_bytes = if (bytes.length() >= 2 && bytes[0] == 48 && (bytes[1] == 120 || bytes[1] == 88)) {
|
|
49
|
+
let mut result = vector[];
|
|
50
|
+
let mut i = 2;
|
|
51
|
+
while (i < bytes.length()) {
|
|
52
|
+
result.push_back(bytes[i]);
|
|
53
|
+
i = i + 1;
|
|
54
|
+
};
|
|
55
|
+
result
|
|
56
|
+
} else {
|
|
57
|
+
bytes
|
|
58
|
+
};
|
|
59
|
+
hex::decode(hex_bytes)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fun base58_decode(input: String): vector<u8> {
|
|
63
|
+
let base58_alphabet = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
64
|
+
let input_bytes = input.as_bytes();
|
|
65
|
+
let len = input_bytes.length();
|
|
66
|
+
|
|
67
|
+
let mut result: vector<u8> = vector[];
|
|
68
|
+
let mut j = 0;
|
|
69
|
+
while (j < SOLANA_ADDRESS_LENGTH) {
|
|
70
|
+
result.push_back(0u8);
|
|
71
|
+
j = j + 1;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
let mut i = 0;
|
|
75
|
+
while (i < len) {
|
|
76
|
+
let c = input_bytes[i];
|
|
77
|
+
let mut char_value: u64 = 0;
|
|
78
|
+
let mut found = false;
|
|
79
|
+
let mut k = 0;
|
|
80
|
+
while (k < 58) {
|
|
81
|
+
if (base58_alphabet[k] == c) {
|
|
82
|
+
char_value = k;
|
|
83
|
+
found = true;
|
|
84
|
+
break
|
|
85
|
+
};
|
|
86
|
+
k = k + 1;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
assert!(found, E_INVALID_SOLANA_ADDRESS);
|
|
90
|
+
|
|
91
|
+
let mut carry = char_value;
|
|
92
|
+
let mut m = (SOLANA_ADDRESS_LENGTH - 1);
|
|
93
|
+
while (m < SOLANA_ADDRESS_LENGTH) {
|
|
94
|
+
let byte_ref = vector::borrow_mut(&mut result, m);
|
|
95
|
+
let tmp = (*byte_ref as u64) * 58 + carry;
|
|
96
|
+
carry = tmp / 256;
|
|
97
|
+
*byte_ref = ((tmp % 256) as u8);
|
|
98
|
+
if (m == 0) break;
|
|
99
|
+
m = m - 1;
|
|
100
|
+
};
|
|
101
|
+
i = i + 1;
|
|
102
|
+
};
|
|
103
|
+
result
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// Convert EVM address to SUI address
|
|
107
|
+
/// Format: [12 zero bytes][20 bytes EVM address]
|
|
108
|
+
public fun evm_to_sui(evm_address_str: String): address {
|
|
109
|
+
let evm_bytes = hex_string_to_bytes(evm_address_str);
|
|
110
|
+
assert!(evm_bytes.length() == EVM_ADDRESS_LENGTH, E_INVALID_EVM_ADDRESS);
|
|
111
|
+
|
|
112
|
+
let mut sui_bytes = vector[];
|
|
113
|
+
let mut i = 0;
|
|
114
|
+
while (i < 12) {
|
|
115
|
+
sui_bytes.push_back(0u8);
|
|
116
|
+
i = i + 1;
|
|
117
|
+
};
|
|
118
|
+
sui_bytes.append(evm_bytes);
|
|
119
|
+
address::from_bytes(sui_bytes)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/// Convert Solana address to SUI address
|
|
123
|
+
/// Direct use of 32 bytes from Base58 decode
|
|
124
|
+
public fun solana_to_sui(solana_address_str: String): address {
|
|
125
|
+
let solana_bytes = base58_decode(solana_address_str);
|
|
126
|
+
assert!(solana_bytes.length() == SOLANA_ADDRESS_LENGTH, E_INVALID_SOLANA_ADDRESS);
|
|
127
|
+
address::from_bytes(solana_bytes)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fun base58_encode(input: vector<u8>): String {
|
|
131
|
+
let base58_alphabet = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
132
|
+
let mut result = vector::empty<u8>();
|
|
133
|
+
let mut num = input;
|
|
134
|
+
|
|
135
|
+
let mut leading_zeros = 0;
|
|
136
|
+
let mut i = 0;
|
|
137
|
+
while (i < num.length()) {
|
|
138
|
+
if (num[i] == 0) {
|
|
139
|
+
leading_zeros = leading_zeros + 1;
|
|
140
|
+
i = i + 1;
|
|
141
|
+
} else {
|
|
142
|
+
break
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
while (!is_zero(&num)) {
|
|
147
|
+
let remainder = div_mod_58(&mut num);
|
|
148
|
+
result.push_back(base58_alphabet[remainder]);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
let mut j = 0;
|
|
152
|
+
while (j < leading_zeros) {
|
|
153
|
+
result.push_back(49);
|
|
154
|
+
j = j + 1;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
vector::reverse(&mut result);
|
|
158
|
+
result.to_ascii_string()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
fun is_zero(num: &vector<u8>): bool {
|
|
162
|
+
let mut i = 0;
|
|
163
|
+
while (i < num.length()) {
|
|
164
|
+
if (num[i] != 0) {
|
|
165
|
+
return false
|
|
166
|
+
};
|
|
167
|
+
i = i + 1;
|
|
168
|
+
};
|
|
169
|
+
true
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
fun div_mod_58(num: &mut vector<u8>): u64 {
|
|
173
|
+
let mut remainder: u64 = 0;
|
|
174
|
+
let mut i = 0;
|
|
175
|
+
while (i < num.length()) {
|
|
176
|
+
let byte_ref = vector::borrow_mut(num, i);
|
|
177
|
+
let current = (remainder * 256) + (*byte_ref as u64);
|
|
178
|
+
*byte_ref = ((current / 58) as u8);
|
|
179
|
+
remainder = current % 58;
|
|
180
|
+
i = i + 1;
|
|
181
|
+
};
|
|
182
|
+
remainder
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// Get original address format based on tx_hash detection
|
|
186
|
+
/// Returns: EVM (0x...), Solana (Base58), or SUI (0x...) format
|
|
187
|
+
public fun ensure_origin(ctx: &TxContext): String {
|
|
188
|
+
let sui_address = ctx.sender();
|
|
189
|
+
let address_bytes = address::to_bytes(sui_address);
|
|
190
|
+
let tx_hash = ctx.digest();
|
|
191
|
+
let chain_type = detect_chain_type_from_tx_hash(tx_hash);
|
|
192
|
+
|
|
193
|
+
if (chain_type == 1) {
|
|
194
|
+
let mut evm_bytes = vector[];
|
|
195
|
+
let mut j = 12;
|
|
196
|
+
while (j < TX_HASH_LENGTH) {
|
|
197
|
+
evm_bytes.push_back(address_bytes[j]);
|
|
198
|
+
j = j + 1;
|
|
199
|
+
};
|
|
200
|
+
let hex_bytes = hex::encode(evm_bytes);
|
|
201
|
+
let mut result_bytes = b"0x";
|
|
202
|
+
result_bytes.append(hex_bytes);
|
|
203
|
+
result_bytes.to_ascii_string()
|
|
204
|
+
} else if (chain_type == 2) {
|
|
205
|
+
base58_encode(address_bytes)
|
|
206
|
+
} else {
|
|
207
|
+
let hex_bytes = hex::encode(address_bytes);
|
|
208
|
+
let mut result_bytes = b"0x";
|
|
209
|
+
result_bytes.append(hex_bytes);
|
|
210
|
+
result_bytes.to_ascii_string()
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// Check if transaction is from EVM chain
|
|
215
|
+
public fun is_evm_address(ctx: &TxContext): bool {
|
|
216
|
+
let tx_hash = ctx.digest();
|
|
217
|
+
detect_chain_type_from_tx_hash(tx_hash) == 1
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// Check if transaction is from Solana
|
|
221
|
+
public fun is_solana_address(ctx: &TxContext): bool {
|
|
222
|
+
let tx_hash = ctx.digest();
|
|
223
|
+
detect_chain_type_from_tx_hash(tx_hash) == 2
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/// Check if transaction is native SUI
|
|
227
|
+
public fun is_sui_address(ctx: &TxContext): bool {
|
|
228
|
+
let tx_hash = ctx.digest();
|
|
229
|
+
detect_chain_type_from_tx_hash(tx_hash) == 0
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ========== Test Utilities ==========
|
|
233
|
+
|
|
234
|
+
#[test_only]
|
|
235
|
+
/// Setup test scenario with EVM context
|
|
236
|
+
/// Input: EVM address as byte string, e.g., b"0x9168765EE952de7C6f8fC6FaD5Ec209B960b7622"
|
|
237
|
+
/// Format: [0xDB][0xDB][0x01][0xE1][...28 bytes...]
|
|
238
|
+
public fun setup_evm_scenario(scenario: &mut test_scenario::Scenario, evm_address_bytes: vector<u8>) {
|
|
239
|
+
use std::ascii;
|
|
240
|
+
|
|
241
|
+
// Convert EVM address string to SUI address
|
|
242
|
+
let evm_address_str = ascii::string(evm_address_bytes);
|
|
243
|
+
let sender = evm_to_sui(evm_address_str);
|
|
244
|
+
|
|
245
|
+
// Generate EVM tx_hash with Dubhe prefix at the beginning
|
|
246
|
+
let mut tx_hash = vector::empty<u8>();
|
|
247
|
+
|
|
248
|
+
// Mark as EVM with Dubhe prefix, version, and chain type
|
|
249
|
+
tx_hash.push_back(DUBHE_PREFIX); // 0xDB
|
|
250
|
+
tx_hash.push_back(DUBHE_PREFIX); // 0xDB
|
|
251
|
+
tx_hash.push_back(DUBHE_VERSION); // 0x01
|
|
252
|
+
tx_hash.push_back(CHAIN_TYPE_EVM); // 0xE1
|
|
253
|
+
|
|
254
|
+
// Fill remaining 28 bytes
|
|
255
|
+
let mut i = 0;
|
|
256
|
+
while (i < 28) {
|
|
257
|
+
tx_hash.push_back((i as u8));
|
|
258
|
+
i = i + 1;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// Set context
|
|
262
|
+
let ctx = test_scenario::ctx(scenario);
|
|
263
|
+
*ctx = tx_context::new(sender, tx_hash, 0, 0, 0);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
#[test_only]
|
|
267
|
+
/// Setup test scenario with Solana context
|
|
268
|
+
/// Input: Solana address as byte string, e.g., b"3vy8k1NAc3Q9EPvqrAuS4DG4qwbgVqfxznEdtcrL743L"
|
|
269
|
+
/// Format: [0xDB][0xDB][0x01][0xE2][...28 bytes...]
|
|
270
|
+
public fun setup_solana_scenario(scenario: &mut test_scenario::Scenario, solana_address_bytes: vector<u8>) {
|
|
271
|
+
use std::ascii;
|
|
272
|
+
|
|
273
|
+
// Convert Solana address string to SUI address
|
|
274
|
+
let solana_address_str = ascii::string(solana_address_bytes);
|
|
275
|
+
let sender = solana_to_sui(solana_address_str);
|
|
276
|
+
|
|
277
|
+
// Generate Solana tx_hash with Dubhe prefix at the beginning
|
|
278
|
+
let mut tx_hash = vector::empty<u8>();
|
|
279
|
+
|
|
280
|
+
// Mark as Solana with Dubhe prefix, version, and chain type
|
|
281
|
+
tx_hash.push_back(DUBHE_PREFIX); // 0xDB
|
|
282
|
+
tx_hash.push_back(DUBHE_PREFIX); // 0xDB
|
|
283
|
+
tx_hash.push_back(DUBHE_VERSION); // 0x01
|
|
284
|
+
tx_hash.push_back(CHAIN_TYPE_SOLANA); // 0xE2
|
|
285
|
+
|
|
286
|
+
// Fill remaining 28 bytes
|
|
287
|
+
let mut i = 0;
|
|
288
|
+
while (i < 28) {
|
|
289
|
+
tx_hash.push_back(((i + 100) as u8));
|
|
290
|
+
i = i + 1;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// Set context
|
|
294
|
+
let ctx = test_scenario::ctx(scenario);
|
|
295
|
+
*ctx = tx_context::new(sender, tx_hash, 0, 0, 0);
|
|
296
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#[test_only]
|
|
2
|
+
module dubhe::address_test;
|
|
3
|
+
|
|
4
|
+
use dubhe::address_system;
|
|
5
|
+
use sui::test_scenario;
|
|
6
|
+
use std::ascii::string;
|
|
7
|
+
|
|
8
|
+
#[test]
|
|
9
|
+
public fun test_address_conversion() {
|
|
10
|
+
let sui_sender = @0x1462cab50fe5998f8161378e5265f7920bfd9fbce604d602619962f608837217;
|
|
11
|
+
let sui_origin_string = string(b"0x1462cab50fe5998f8161378e5265f7920bfd9fbce604d602619962f608837217");
|
|
12
|
+
let evm_origin_string = string(b"0x9168765ee952de7c6f8fc6fad5ec209b960b7622");
|
|
13
|
+
let solana_origin_string = string(b"3vy8k1NAc3Q9EPvqrAuS4DG4qwbgVqfxznEdtcrL743L");
|
|
14
|
+
let mut scenario = test_scenario::begin(sui_sender);
|
|
15
|
+
|
|
16
|
+
// Test EVM address conversion
|
|
17
|
+
std::debug::print(&string(b"EVM address:"));
|
|
18
|
+
std::debug::print(&evm_origin_string);
|
|
19
|
+
let evm_sui_address = address_system::evm_to_sui(evm_origin_string);
|
|
20
|
+
std::debug::print(&string(b"EVM->SUI:"));
|
|
21
|
+
std::debug::print(&evm_sui_address.to_ascii_string());
|
|
22
|
+
|
|
23
|
+
// Test Solana address conversion
|
|
24
|
+
let solana_address_str = string(b"3vy8k1NAc3Q9EPvqrAuS4DG4qwbgVqfxznEdtcrL743L");
|
|
25
|
+
let solana_sui_address = address_system::solana_to_sui(solana_address_str);
|
|
26
|
+
std::debug::print(&string(b"Solana->SUI:"));
|
|
27
|
+
std::debug::print(&solana_sui_address.to_ascii_string());
|
|
28
|
+
|
|
29
|
+
// Test SUI address detection
|
|
30
|
+
{
|
|
31
|
+
let ctx = test_scenario::ctx(&mut scenario);
|
|
32
|
+
assert!(address_system::is_sui_address(ctx));
|
|
33
|
+
assert!(address_system::ensure_origin(ctx) == sui_origin_string);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Test EVM address detection
|
|
37
|
+
address_system::setup_evm_scenario(&mut scenario, b"0x9168765EE952de7C6f8fC6FaD5Ec209B960b7622");
|
|
38
|
+
{
|
|
39
|
+
let ctx = test_scenario::ctx(&mut scenario);
|
|
40
|
+
assert!(address_system::is_evm_address(ctx));
|
|
41
|
+
std::debug::print(&string(b"evm_origin_string:"));
|
|
42
|
+
std::debug::print(&address_system::ensure_origin(ctx));
|
|
43
|
+
assert!(address_system::ensure_origin(ctx) == evm_origin_string);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Test Solana address detection
|
|
47
|
+
address_system::setup_solana_scenario(&mut scenario, b"3vy8k1NAc3Q9EPvqrAuS4DG4qwbgVqfxznEdtcrL743L");
|
|
48
|
+
{
|
|
49
|
+
let ctx = test_scenario::ctx(&mut scenario);
|
|
50
|
+
assert!(address_system::is_solana_address(ctx));
|
|
51
|
+
assert!(address_system::ensure_origin(ctx) == solana_origin_string);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
scenario.end();
|
|
55
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[move]
|
|
4
4
|
version = 3
|
|
5
|
-
manifest_digest = "
|
|
5
|
+
manifest_digest = "73896F00312A2E2C47F56DCCABF824967268FF718658DD5D0FFFE2E60824F94A"
|
|
6
6
|
deps_digest = "F8BBB0CCB2491CA29A3DF03D6F92277A4F3574266507ACD77214D37ECA3F3082"
|
|
7
7
|
dependencies = [
|
|
8
8
|
{ id = "Sui", name = "Sui" },
|
|
@@ -28,5 +28,5 @@ flavor = "sui"
|
|
|
28
28
|
[env.testnet]
|
|
29
29
|
chain-id = "4c78adac"
|
|
30
30
|
original-published-id = "0xa09cd4137e604ec5a7a88f72c572ecd064b0e713a3fbf705a88456cdbccf36c0"
|
|
31
|
-
latest-published-id = "
|
|
32
|
-
published-version = "
|
|
31
|
+
latest-published-id = "0x4613b8a390298cc450fe11360fddf41ba1bde38060163c5ca9f192a25b4f9f2e"
|
|
32
|
+
published-version = "3"
|
|
@@ -4,7 +4,7 @@ edition = "2024"
|
|
|
4
4
|
version = "1.0.0"
|
|
5
5
|
license = "apache2.0"
|
|
6
6
|
authors = ["Obelisk Labs"]
|
|
7
|
-
published-at = "
|
|
7
|
+
published-at = "0x4613b8a390298cc450fe11360fddf41ba1bde38060163c5ca9f192a25b4f9f2e"
|
|
8
8
|
|
|
9
9
|
[dependencies]
|
|
10
10
|
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "mainnet-v1.46.3" }
|
package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/codegen/genesis.move
CHANGED
|
@@ -82,8 +82,7 @@
|
|
|
82
82
|
dapp_system::upgrade_dapp(dapp_hub, dapp_key, new_package_id, new_version, ctx);
|
|
83
83
|
// Register new tables
|
|
84
84
|
// ==========================================
|
|
85
|
-
|
|
86
|
-
asset_remove_liquidity::register_table(dapp_hub, ctx);
|
|
85
|
+
|
|
87
86
|
// ==========================================
|
|
88
87
|
}
|
|
89
88
|
}
|
package/templates/nextjs/sui-template/packages/contracts/src/dubhe/sources/scripts/migrate.move
CHANGED
|
@@ -2,7 +2,7 @@ module dubhe::migrate {
|
|
|
2
2
|
use dubhe::dapp_service::DappHub;
|
|
3
3
|
use dubhe::genesis;
|
|
4
4
|
|
|
5
|
-
const ON_CHAIN_VERSION: u32 =
|
|
5
|
+
const ON_CHAIN_VERSION: u32 = 3;
|
|
6
6
|
|
|
7
7
|
public fun on_chain_version(): u32 {
|
|
8
8
|
ON_CHAIN_VERSION
|
|
@@ -11,4 +11,8 @@ module dubhe::migrate {
|
|
|
11
11
|
public entry fun migrate_to_v2(dapp_hub: &mut DappHub, new_package_id: address, new_version: u32, ctx: &mut TxContext) {
|
|
12
12
|
genesis::upgrade(dapp_hub, new_package_id, new_version, ctx);
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
public entry fun migrate_to_v3(dapp_hub: &mut DappHub, new_package_id: address, new_version: u32, ctx: &mut TxContext) {
|
|
16
|
+
genesis::upgrade(dapp_hub, new_package_id, new_version, ctx);
|
|
17
|
+
}
|
|
14
18
|
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
module dubhe::address_system;
|
|
2
|
+
|
|
3
|
+
use std::ascii::String;
|
|
4
|
+
use sui::address;
|
|
5
|
+
use sui::hex;
|
|
6
|
+
|
|
7
|
+
#[test_only]
|
|
8
|
+
use sui::test_scenario;
|
|
9
|
+
|
|
10
|
+
// TX_HASH signature markers
|
|
11
|
+
const DUBHE_PREFIX: u8 = 0xDB;
|
|
12
|
+
const DUBHE_VERSION: u8 = 0x01;
|
|
13
|
+
const CHAIN_TYPE_EVM: u8 = 0xE1;
|
|
14
|
+
const CHAIN_TYPE_SOLANA: u8 = 0xE2;
|
|
15
|
+
|
|
16
|
+
// Constants
|
|
17
|
+
const TX_HASH_LENGTH: u64 = 32;
|
|
18
|
+
const SOLANA_ADDRESS_LENGTH: u64 = 32;
|
|
19
|
+
const EVM_ADDRESS_LENGTH: u64 = 20;
|
|
20
|
+
|
|
21
|
+
// Error codes
|
|
22
|
+
const E_INVALID_EVM_ADDRESS: u64 = 1;
|
|
23
|
+
const E_INVALID_SOLANA_ADDRESS: u64 = 2;
|
|
24
|
+
|
|
25
|
+
/// Detect chain type from tx_hash signature
|
|
26
|
+
/// Returns: 0 = SUI, 1 = EVM, 2 = Solana
|
|
27
|
+
/// Format: [0xDB][0xDB][0x01][CHAIN_TYPE][...28 bytes...]
|
|
28
|
+
fun detect_chain_type_from_tx_hash(tx_hash: &vector<u8>): u8 {
|
|
29
|
+
if (tx_hash.length() != TX_HASH_LENGTH ||
|
|
30
|
+
tx_hash[0] != DUBHE_PREFIX ||
|
|
31
|
+
tx_hash[1] != DUBHE_PREFIX ||
|
|
32
|
+
tx_hash[2] != DUBHE_VERSION) {
|
|
33
|
+
return 0
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let chain_type = tx_hash[3];
|
|
37
|
+
if (chain_type == CHAIN_TYPE_EVM) {
|
|
38
|
+
1
|
|
39
|
+
} else if (chain_type == CHAIN_TYPE_SOLANA) {
|
|
40
|
+
2
|
|
41
|
+
} else {
|
|
42
|
+
0
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fun hex_string_to_bytes(hex_str: String): vector<u8> {
|
|
47
|
+
let bytes = hex_str.into_bytes();
|
|
48
|
+
let hex_bytes = if (bytes.length() >= 2 && bytes[0] == 48 && (bytes[1] == 120 || bytes[1] == 88)) {
|
|
49
|
+
let mut result = vector[];
|
|
50
|
+
let mut i = 2;
|
|
51
|
+
while (i < bytes.length()) {
|
|
52
|
+
result.push_back(bytes[i]);
|
|
53
|
+
i = i + 1;
|
|
54
|
+
};
|
|
55
|
+
result
|
|
56
|
+
} else {
|
|
57
|
+
bytes
|
|
58
|
+
};
|
|
59
|
+
hex::decode(hex_bytes)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fun base58_decode(input: String): vector<u8> {
|
|
63
|
+
let base58_alphabet = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
64
|
+
let input_bytes = input.as_bytes();
|
|
65
|
+
let len = input_bytes.length();
|
|
66
|
+
|
|
67
|
+
let mut result: vector<u8> = vector[];
|
|
68
|
+
let mut j = 0;
|
|
69
|
+
while (j < SOLANA_ADDRESS_LENGTH) {
|
|
70
|
+
result.push_back(0u8);
|
|
71
|
+
j = j + 1;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
let mut i = 0;
|
|
75
|
+
while (i < len) {
|
|
76
|
+
let c = input_bytes[i];
|
|
77
|
+
let mut char_value: u64 = 0;
|
|
78
|
+
let mut found = false;
|
|
79
|
+
let mut k = 0;
|
|
80
|
+
while (k < 58) {
|
|
81
|
+
if (base58_alphabet[k] == c) {
|
|
82
|
+
char_value = k;
|
|
83
|
+
found = true;
|
|
84
|
+
break
|
|
85
|
+
};
|
|
86
|
+
k = k + 1;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
assert!(found, E_INVALID_SOLANA_ADDRESS);
|
|
90
|
+
|
|
91
|
+
let mut carry = char_value;
|
|
92
|
+
let mut m = (SOLANA_ADDRESS_LENGTH - 1);
|
|
93
|
+
while (m < SOLANA_ADDRESS_LENGTH) {
|
|
94
|
+
let byte_ref = vector::borrow_mut(&mut result, m);
|
|
95
|
+
let tmp = (*byte_ref as u64) * 58 + carry;
|
|
96
|
+
carry = tmp / 256;
|
|
97
|
+
*byte_ref = ((tmp % 256) as u8);
|
|
98
|
+
if (m == 0) break;
|
|
99
|
+
m = m - 1;
|
|
100
|
+
};
|
|
101
|
+
i = i + 1;
|
|
102
|
+
};
|
|
103
|
+
result
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// Convert EVM address to SUI address
|
|
107
|
+
/// Format: [12 zero bytes][20 bytes EVM address]
|
|
108
|
+
public fun evm_to_sui(evm_address_str: String): address {
|
|
109
|
+
let evm_bytes = hex_string_to_bytes(evm_address_str);
|
|
110
|
+
assert!(evm_bytes.length() == EVM_ADDRESS_LENGTH, E_INVALID_EVM_ADDRESS);
|
|
111
|
+
|
|
112
|
+
let mut sui_bytes = vector[];
|
|
113
|
+
let mut i = 0;
|
|
114
|
+
while (i < 12) {
|
|
115
|
+
sui_bytes.push_back(0u8);
|
|
116
|
+
i = i + 1;
|
|
117
|
+
};
|
|
118
|
+
sui_bytes.append(evm_bytes);
|
|
119
|
+
address::from_bytes(sui_bytes)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/// Convert Solana address to SUI address
|
|
123
|
+
/// Direct use of 32 bytes from Base58 decode
|
|
124
|
+
public fun solana_to_sui(solana_address_str: String): address {
|
|
125
|
+
let solana_bytes = base58_decode(solana_address_str);
|
|
126
|
+
assert!(solana_bytes.length() == SOLANA_ADDRESS_LENGTH, E_INVALID_SOLANA_ADDRESS);
|
|
127
|
+
address::from_bytes(solana_bytes)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fun base58_encode(input: vector<u8>): String {
|
|
131
|
+
let base58_alphabet = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
132
|
+
let mut result = vector::empty<u8>();
|
|
133
|
+
let mut num = input;
|
|
134
|
+
|
|
135
|
+
let mut leading_zeros = 0;
|
|
136
|
+
let mut i = 0;
|
|
137
|
+
while (i < num.length()) {
|
|
138
|
+
if (num[i] == 0) {
|
|
139
|
+
leading_zeros = leading_zeros + 1;
|
|
140
|
+
i = i + 1;
|
|
141
|
+
} else {
|
|
142
|
+
break
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
while (!is_zero(&num)) {
|
|
147
|
+
let remainder = div_mod_58(&mut num);
|
|
148
|
+
result.push_back(base58_alphabet[remainder]);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
let mut j = 0;
|
|
152
|
+
while (j < leading_zeros) {
|
|
153
|
+
result.push_back(49);
|
|
154
|
+
j = j + 1;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
vector::reverse(&mut result);
|
|
158
|
+
result.to_ascii_string()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
fun is_zero(num: &vector<u8>): bool {
|
|
162
|
+
let mut i = 0;
|
|
163
|
+
while (i < num.length()) {
|
|
164
|
+
if (num[i] != 0) {
|
|
165
|
+
return false
|
|
166
|
+
};
|
|
167
|
+
i = i + 1;
|
|
168
|
+
};
|
|
169
|
+
true
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
fun div_mod_58(num: &mut vector<u8>): u64 {
|
|
173
|
+
let mut remainder: u64 = 0;
|
|
174
|
+
let mut i = 0;
|
|
175
|
+
while (i < num.length()) {
|
|
176
|
+
let byte_ref = vector::borrow_mut(num, i);
|
|
177
|
+
let current = (remainder * 256) + (*byte_ref as u64);
|
|
178
|
+
*byte_ref = ((current / 58) as u8);
|
|
179
|
+
remainder = current % 58;
|
|
180
|
+
i = i + 1;
|
|
181
|
+
};
|
|
182
|
+
remainder
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// Get original address format based on tx_hash detection
|
|
186
|
+
/// Returns: EVM (0x...), Solana (Base58), or SUI (0x...) format
|
|
187
|
+
public fun ensure_origin(ctx: &TxContext): String {
|
|
188
|
+
let sui_address = ctx.sender();
|
|
189
|
+
let address_bytes = address::to_bytes(sui_address);
|
|
190
|
+
let tx_hash = ctx.digest();
|
|
191
|
+
let chain_type = detect_chain_type_from_tx_hash(tx_hash);
|
|
192
|
+
|
|
193
|
+
if (chain_type == 1) {
|
|
194
|
+
let mut evm_bytes = vector[];
|
|
195
|
+
let mut j = 12;
|
|
196
|
+
while (j < TX_HASH_LENGTH) {
|
|
197
|
+
evm_bytes.push_back(address_bytes[j]);
|
|
198
|
+
j = j + 1;
|
|
199
|
+
};
|
|
200
|
+
let hex_bytes = hex::encode(evm_bytes);
|
|
201
|
+
let mut result_bytes = b"0x";
|
|
202
|
+
result_bytes.append(hex_bytes);
|
|
203
|
+
result_bytes.to_ascii_string()
|
|
204
|
+
} else if (chain_type == 2) {
|
|
205
|
+
base58_encode(address_bytes)
|
|
206
|
+
} else {
|
|
207
|
+
let hex_bytes = hex::encode(address_bytes);
|
|
208
|
+
let mut result_bytes = b"0x";
|
|
209
|
+
result_bytes.append(hex_bytes);
|
|
210
|
+
result_bytes.to_ascii_string()
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// Check if transaction is from EVM chain
|
|
215
|
+
public fun is_evm_address(ctx: &TxContext): bool {
|
|
216
|
+
let tx_hash = ctx.digest();
|
|
217
|
+
detect_chain_type_from_tx_hash(tx_hash) == 1
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// Check if transaction is from Solana
|
|
221
|
+
public fun is_solana_address(ctx: &TxContext): bool {
|
|
222
|
+
let tx_hash = ctx.digest();
|
|
223
|
+
detect_chain_type_from_tx_hash(tx_hash) == 2
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/// Check if transaction is native SUI
|
|
227
|
+
public fun is_sui_address(ctx: &TxContext): bool {
|
|
228
|
+
let tx_hash = ctx.digest();
|
|
229
|
+
detect_chain_type_from_tx_hash(tx_hash) == 0
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ========== Test Utilities ==========
|
|
233
|
+
|
|
234
|
+
#[test_only]
|
|
235
|
+
/// Setup test scenario with EVM context
|
|
236
|
+
/// Input: EVM address as byte string, e.g., b"0x9168765EE952de7C6f8fC6FaD5Ec209B960b7622"
|
|
237
|
+
/// Format: [0xDB][0xDB][0x01][0xE1][...28 bytes...]
|
|
238
|
+
public fun setup_evm_scenario(scenario: &mut test_scenario::Scenario, evm_address_bytes: vector<u8>) {
|
|
239
|
+
use std::ascii;
|
|
240
|
+
|
|
241
|
+
// Convert EVM address string to SUI address
|
|
242
|
+
let evm_address_str = ascii::string(evm_address_bytes);
|
|
243
|
+
let sender = evm_to_sui(evm_address_str);
|
|
244
|
+
|
|
245
|
+
// Generate EVM tx_hash with Dubhe prefix at the beginning
|
|
246
|
+
let mut tx_hash = vector::empty<u8>();
|
|
247
|
+
|
|
248
|
+
// Mark as EVM with Dubhe prefix, version, and chain type
|
|
249
|
+
tx_hash.push_back(DUBHE_PREFIX); // 0xDB
|
|
250
|
+
tx_hash.push_back(DUBHE_PREFIX); // 0xDB
|
|
251
|
+
tx_hash.push_back(DUBHE_VERSION); // 0x01
|
|
252
|
+
tx_hash.push_back(CHAIN_TYPE_EVM); // 0xE1
|
|
253
|
+
|
|
254
|
+
// Fill remaining 28 bytes
|
|
255
|
+
let mut i = 0;
|
|
256
|
+
while (i < 28) {
|
|
257
|
+
tx_hash.push_back((i as u8));
|
|
258
|
+
i = i + 1;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// Set context
|
|
262
|
+
let ctx = test_scenario::ctx(scenario);
|
|
263
|
+
*ctx = tx_context::new(sender, tx_hash, 0, 0, 0);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
#[test_only]
|
|
267
|
+
/// Setup test scenario with Solana context
|
|
268
|
+
/// Input: Solana address as byte string, e.g., b"3vy8k1NAc3Q9EPvqrAuS4DG4qwbgVqfxznEdtcrL743L"
|
|
269
|
+
/// Format: [0xDB][0xDB][0x01][0xE2][...28 bytes...]
|
|
270
|
+
public fun setup_solana_scenario(scenario: &mut test_scenario::Scenario, solana_address_bytes: vector<u8>) {
|
|
271
|
+
use std::ascii;
|
|
272
|
+
|
|
273
|
+
// Convert Solana address string to SUI address
|
|
274
|
+
let solana_address_str = ascii::string(solana_address_bytes);
|
|
275
|
+
let sender = solana_to_sui(solana_address_str);
|
|
276
|
+
|
|
277
|
+
// Generate Solana tx_hash with Dubhe prefix at the beginning
|
|
278
|
+
let mut tx_hash = vector::empty<u8>();
|
|
279
|
+
|
|
280
|
+
// Mark as Solana with Dubhe prefix, version, and chain type
|
|
281
|
+
tx_hash.push_back(DUBHE_PREFIX); // 0xDB
|
|
282
|
+
tx_hash.push_back(DUBHE_PREFIX); // 0xDB
|
|
283
|
+
tx_hash.push_back(DUBHE_VERSION); // 0x01
|
|
284
|
+
tx_hash.push_back(CHAIN_TYPE_SOLANA); // 0xE2
|
|
285
|
+
|
|
286
|
+
// Fill remaining 28 bytes
|
|
287
|
+
let mut i = 0;
|
|
288
|
+
while (i < 28) {
|
|
289
|
+
tx_hash.push_back(((i + 100) as u8));
|
|
290
|
+
i = i + 1;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// Set context
|
|
294
|
+
let ctx = test_scenario::ctx(scenario);
|
|
295
|
+
*ctx = tx_context::new(sender, tx_hash, 0, 0, 0);
|
|
296
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#[test_only]
|
|
2
|
+
module dubhe::address_test;
|
|
3
|
+
|
|
4
|
+
use dubhe::address_system;
|
|
5
|
+
use sui::test_scenario;
|
|
6
|
+
use std::ascii::string;
|
|
7
|
+
|
|
8
|
+
#[test]
|
|
9
|
+
public fun test_address_conversion() {
|
|
10
|
+
let sui_sender = @0x1462cab50fe5998f8161378e5265f7920bfd9fbce604d602619962f608837217;
|
|
11
|
+
let sui_origin_string = string(b"0x1462cab50fe5998f8161378e5265f7920bfd9fbce604d602619962f608837217");
|
|
12
|
+
let evm_origin_string = string(b"0x9168765ee952de7c6f8fc6fad5ec209b960b7622");
|
|
13
|
+
let solana_origin_string = string(b"3vy8k1NAc3Q9EPvqrAuS4DG4qwbgVqfxznEdtcrL743L");
|
|
14
|
+
let mut scenario = test_scenario::begin(sui_sender);
|
|
15
|
+
|
|
16
|
+
// Test EVM address conversion
|
|
17
|
+
std::debug::print(&string(b"EVM address:"));
|
|
18
|
+
std::debug::print(&evm_origin_string);
|
|
19
|
+
let evm_sui_address = address_system::evm_to_sui(evm_origin_string);
|
|
20
|
+
std::debug::print(&string(b"EVM->SUI:"));
|
|
21
|
+
std::debug::print(&evm_sui_address.to_ascii_string());
|
|
22
|
+
|
|
23
|
+
// Test Solana address conversion
|
|
24
|
+
let solana_address_str = string(b"3vy8k1NAc3Q9EPvqrAuS4DG4qwbgVqfxznEdtcrL743L");
|
|
25
|
+
let solana_sui_address = address_system::solana_to_sui(solana_address_str);
|
|
26
|
+
std::debug::print(&string(b"Solana->SUI:"));
|
|
27
|
+
std::debug::print(&solana_sui_address.to_ascii_string());
|
|
28
|
+
|
|
29
|
+
// Test SUI address detection
|
|
30
|
+
{
|
|
31
|
+
let ctx = test_scenario::ctx(&mut scenario);
|
|
32
|
+
assert!(address_system::is_sui_address(ctx));
|
|
33
|
+
assert!(address_system::ensure_origin(ctx) == sui_origin_string);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Test EVM address detection
|
|
37
|
+
address_system::setup_evm_scenario(&mut scenario, b"0x9168765EE952de7C6f8fC6FaD5Ec209B960b7622");
|
|
38
|
+
{
|
|
39
|
+
let ctx = test_scenario::ctx(&mut scenario);
|
|
40
|
+
assert!(address_system::is_evm_address(ctx));
|
|
41
|
+
std::debug::print(&string(b"evm_origin_string:"));
|
|
42
|
+
std::debug::print(&address_system::ensure_origin(ctx));
|
|
43
|
+
assert!(address_system::ensure_origin(ctx) == evm_origin_string);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Test Solana address detection
|
|
47
|
+
address_system::setup_solana_scenario(&mut scenario, b"3vy8k1NAc3Q9EPvqrAuS4DG4qwbgVqfxznEdtcrL743L");
|
|
48
|
+
{
|
|
49
|
+
let ctx = test_scenario::ctx(&mut scenario);
|
|
50
|
+
assert!(address_system::is_solana_address(ctx));
|
|
51
|
+
assert!(address_system::ensure_origin(ctx) == solana_origin_string);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
scenario.end();
|
|
55
|
+
}
|