@biochain/dweb-compat 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+
2
+ > @biochain/dweb-compat@0.1.0 typecheck:run /Users/kzf/Dev/bioforestChain/KeyApp/packages/dweb-compat
3
+ > tsc --noEmit
4
+
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@biochain/dweb-compat",
3
+ "version": "0.1.0",
4
+ "description": "dweb-plaoc 到 KeyApp bio provider 的兼容适配器",
5
+ "type": "module",
6
+ "main": "./src/index.ts",
7
+ "types": "./src/index.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./src/index.ts",
11
+ "import": "./src/index.ts"
12
+ },
13
+ "./is-dweb": {
14
+ "types": "./src/is-dweb.ts",
15
+ "import": "./src/is-dweb.ts"
16
+ },
17
+ "./plugins-stub": {
18
+ "types": "./src/plugins-stub.ts",
19
+ "import": "./src/plugins-stub.ts"
20
+ }
21
+ },
22
+ "peerDependencies": {},
23
+ "devDependencies": {
24
+ "typescript": "^5.9.3",
25
+ "vitest": "^4.0.0"
26
+ },
27
+ "scripts": {
28
+ "typecheck": "tsc --noEmit",
29
+ "typecheck:run": "tsc --noEmit",
30
+ "test": "vitest",
31
+ "test:run": "vitest run",
32
+ "test:storybook": "echo 'SDK has no storybook'"
33
+ }
34
+ }
package/src/address.ts ADDED
@@ -0,0 +1,79 @@
1
+ /**
2
+ * 地址授权适配
3
+ *
4
+ * 将 dweb 的 getWalleterAddresss 转换为 KeyApp 的 bio_requestAccounts
5
+ */
6
+
7
+ import { bioRequest } from './bridge'
8
+ import type { $WalletGetAddressResponse, $CHAIN_NAME } from './types'
9
+
10
+ /** KeyApp bio_accounts 返回的账户信息 */
11
+ interface BioAccountInfo {
12
+ address: string
13
+ chain: string
14
+ publicKey?: string
15
+ }
16
+
17
+ /**
18
+ * 获取钱包地址授权
19
+ *
20
+ * 对应 dweb 的 getWalleterAddresss(mmid)
21
+ * 转换为 KeyApp 的 bio_requestAccounts + bio_accounts
22
+ */
23
+ export async function getWalleterAddresss(
24
+ _mmid: `${string}.dweb`
25
+ ): Promise<$WalletGetAddressResponse | null> {
26
+ try {
27
+ // 1. 请求账户授权
28
+ const addresses = await bioRequest<string[]>('bio_requestAccounts')
29
+
30
+ if (!addresses || addresses.length === 0) {
31
+ return null
32
+ }
33
+
34
+ // 2. 获取完整账户信息
35
+ let accounts: BioAccountInfo[]
36
+ try {
37
+ accounts = await bioRequest<BioAccountInfo[]>('bio_accounts')
38
+ } catch {
39
+ // 如果 bio_accounts 失败,使用地址构造基本信息
40
+ accounts = addresses.map(addr => ({ address: addr, chain: 'BFMeta' }))
41
+ }
42
+
43
+ // 3. 转换为 dweb 格式
44
+ return accounts.map(acc => ({
45
+ name: '',
46
+ chainName: acc.chain as $CHAIN_NAME,
47
+ address: acc.address,
48
+ main: acc.address, // KeyApp 中 main 等于 address
49
+ publicKey: acc.publicKey || '',
50
+ privateKey: '', // 不暴露私钥
51
+ magic: '',
52
+ signMessage: '',
53
+ }))
54
+ } catch (error) {
55
+ console.error('[dweb-compat] getWalleterAddresss error:', error)
56
+ return null
57
+ }
58
+ }
59
+
60
+ /**
61
+ * 验证地址导入
62
+ *
63
+ * 对应 dweb 的 verifyAddressImport
64
+ */
65
+ export async function verifyAddressImport(
66
+ _mmid: `${string}.dweb`,
67
+ opts: { address: string; message: string; chainName: $CHAIN_NAME }
68
+ ): Promise<boolean> {
69
+ try {
70
+ const signature = await bioRequest<string>('bio_signMessage', {
71
+ address: opts.address,
72
+ message: opts.message,
73
+ chain: opts.chainName,
74
+ })
75
+ return typeof signature === 'string' && signature.length > 0
76
+ } catch {
77
+ return false
78
+ }
79
+ }
package/src/bridge.ts ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * PostMessage Bridge - 与 KeyApp Bio Provider 通信
3
+ */
4
+
5
+ let requestId = 0
6
+
7
+ export interface BioResponse<T = unknown> {
8
+ success: boolean
9
+ result?: T
10
+ error?: { code: number; message: string }
11
+ }
12
+
13
+ /**
14
+ * 发送 bio_request 到 KeyApp 宿主
15
+ */
16
+ export function bioRequest<T>(method: string, params?: unknown): Promise<T> {
17
+ return new Promise((resolve, reject) => {
18
+ const id = `dweb_compat_${++requestId}_${Date.now()}`
19
+
20
+ const handler = (event: MessageEvent) => {
21
+ const data = event.data
22
+ if (data?.type === 'bio_response' && data.id === id) {
23
+ window.removeEventListener('message', handler)
24
+ if (data.success) {
25
+ resolve(data.result as T)
26
+ } else {
27
+ const error = new Error(data.error?.message || 'Unknown error')
28
+ ; (error as Error & { code?: number }).code = data.error?.code
29
+ reject(error)
30
+ }
31
+ }
32
+ }
33
+
34
+ window.addEventListener('message', handler)
35
+
36
+ // 发送请求到父窗口 (KeyApp)
37
+ const message = {
38
+ type: 'bio_request',
39
+ id,
40
+ method,
41
+ params: params !== undefined ? [params] : [],
42
+ }
43
+
44
+ // 优先发送到 parent (iframe 模式),否则通过 self (web worker 或独立模式)
45
+ const target = window.parent !== window ? window.parent : window
46
+ target.postMessage(message, '*')
47
+
48
+ // 超时处理 (60 秒)
49
+ setTimeout(() => {
50
+ window.removeEventListener('message', handler)
51
+ reject(new Error(`Request timeout: ${method}`))
52
+ }, 60000)
53
+ })
54
+ }
55
+
56
+ /**
57
+ * 监听来自 KeyApp 的事件
58
+ */
59
+ export function onBioEvent(event: string, callback: (...args: unknown[]) => void): () => void {
60
+ const handler = (e: MessageEvent) => {
61
+ if (e.data?.type === 'bio_event' && e.data.event === event) {
62
+ callback(...(e.data.args || []))
63
+ }
64
+ }
65
+
66
+ window.addEventListener('message', handler)
67
+ return () => window.removeEventListener('message', handler)
68
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Crypto Token API 封装
3
+ *
4
+ * 提供 Token 授权和加密操作的便捷函数
5
+ */
6
+
7
+ import { bioRequest } from './bridge'
8
+
9
+ // ==================== 类型定义 ====================
10
+
11
+ export type CryptoAction = 'asymmetricEncrypt' | 'sign'
12
+ export type TokenDuration = '5min' | '15min' | '1hour' | '1day'
13
+
14
+ export interface RequestCryptoTokenParams {
15
+ actions: CryptoAction[]
16
+ duration: TokenDuration
17
+ address: string
18
+ }
19
+
20
+ export interface RequestCryptoTokenResponse {
21
+ tokenId: string
22
+ sessionSecret: string
23
+ expiresAt: number
24
+ grantedActions: CryptoAction[]
25
+ }
26
+
27
+ export interface AsymmetricEncryptParams {
28
+ data: string
29
+ recipientPublicKey: string
30
+ }
31
+
32
+ export interface SignParams {
33
+ data: string
34
+ }
35
+
36
+ export interface CryptoExecuteResponse {
37
+ result: string
38
+ publicKey: string
39
+ }
40
+
41
+ // ==================== Token 操作 ====================
42
+
43
+ /**
44
+ * 请求加密操作授权
45
+ *
46
+ * 用户需要输入手势密码确认授权
47
+ *
48
+ * @param actions 需要的操作权限
49
+ * @param duration 授权时长
50
+ * @param address 使用的地址
51
+ * @param chainId 链 ID(可选,用于 UI 显示)
52
+ */
53
+ export async function requestCryptoToken(
54
+ actions: CryptoAction[],
55
+ duration: TokenDuration,
56
+ address: string,
57
+ chainId?: string
58
+ ): Promise<RequestCryptoTokenResponse> {
59
+ return bioRequest<RequestCryptoTokenResponse>('bio_requestCryptoToken', {
60
+ actions,
61
+ duration,
62
+ address,
63
+ chainId,
64
+ })
65
+ }
66
+
67
+ /**
68
+ * 使用 Token 执行非对称加密
69
+ */
70
+ export async function asymmetricEncrypt(
71
+ tokenId: string,
72
+ sessionSecret: string,
73
+ data: string,
74
+ recipientPublicKey: string
75
+ ): Promise<CryptoExecuteResponse> {
76
+ return bioRequest<CryptoExecuteResponse>('bio_cryptoExecute', {
77
+ tokenId,
78
+ sessionSecret,
79
+ action: 'asymmetricEncrypt',
80
+ params: { data, recipientPublicKey },
81
+ })
82
+ }
83
+
84
+ /**
85
+ * 使用 Token 执行签名
86
+ */
87
+ export async function signData(
88
+ tokenId: string,
89
+ sessionSecret: string,
90
+ data: string
91
+ ): Promise<CryptoExecuteResponse> {
92
+ return bioRequest<CryptoExecuteResponse>('bio_cryptoExecute', {
93
+ tokenId,
94
+ sessionSecret,
95
+ action: 'sign',
96
+ params: { data },
97
+ })
98
+ }
99
+
100
+ // ==================== RWA 登录便捷函数 ====================
101
+
102
+ export interface RwaLoginResult {
103
+ /** 钱包地址 */
104
+ address: string
105
+ /** 用户公钥 (Buffer) */
106
+ publicKey: Buffer
107
+ /** 加密的 signcode (Buffer) */
108
+ signcode: Buffer
109
+ }
110
+
111
+ /**
112
+ * 将各种格式的公钥转换为 hex 字符串
113
+ */
114
+ function normalizeToHex(input: unknown): string {
115
+ if (typeof input === 'string') {
116
+ return input
117
+ }
118
+ // { type: 'Buffer', data: [...] } 格式
119
+ if (input && typeof input === 'object' && 'type' in input && 'data' in input) {
120
+ const bufferLike = input as { type: string; data: number[] }
121
+ if (bufferLike.type === 'Buffer' && Array.isArray(bufferLike.data)) {
122
+ return Buffer.from(bufferLike.data).toString('hex')
123
+ }
124
+ }
125
+ // Uint8Array 或 number[]
126
+ if (Array.isArray(input) || input instanceof Uint8Array) {
127
+ return Buffer.from(input as ArrayLike<number>).toString('hex')
128
+ }
129
+ // Buffer
130
+ if (Buffer.isBuffer(input)) {
131
+ return input.toString('hex')
132
+ }
133
+ throw new Error(`Invalid input format: ${typeof input}`)
134
+ }
135
+
136
+ /**
137
+ * RWA Hub 登录
138
+ *
139
+ * 封装了完整的 RWA 登录流程:
140
+ * 1. 获取钱包地址
141
+ * 2. 请求加密授权
142
+ * 3. 执行非对称加密生成 signcode
143
+ *
144
+ * @param systemPublicKey 系统公钥,支持 hex 字符串、Buffer 对象或字节数组
145
+ */
146
+ export async function rwaLogin(
147
+ systemPublicKey: unknown
148
+ ): Promise<RwaLoginResult> {
149
+ const systemPubKeyHex = normalizeToHex(systemPublicKey)
150
+ // BFMeta 相关链的所有可能名称
151
+ const BFMETA_CHAINS = ['bfmeta', 'bfchainv2', 'bioforest', 'bfmchain', 'bfchain']
152
+
153
+ // 1. 获取钱包地址(指定 BFMeta 链)
154
+ const accounts = await bioRequest<Array<{ address: string; chain: string; publicKey?: string }>>(
155
+ 'bio_requestAccounts',
156
+ { chain: 'bfmeta' } // RWA Hub 需要 BFMeta 链
157
+ )
158
+
159
+ if (!accounts || accounts.length === 0) {
160
+ throw new Error('No accounts available')
161
+ }
162
+
163
+ // 找到 BFMeta 系列链的地址
164
+ const account = accounts.find(a => {
165
+ const chain = a.chain.toLowerCase()
166
+ return BFMETA_CHAINS.includes(chain)
167
+ })
168
+ if (!account) {
169
+ const chains = accounts.map(a => a.chain).join(', ')
170
+ throw new Error(`BFMeta account not found. Available chains: ${chains}`)
171
+ }
172
+
173
+ // 2. 请求加密授权(用户输入手势密码)- 使用实际选中的链 ID
174
+ const { tokenId, sessionSecret } = await requestCryptoToken(
175
+ ['asymmetricEncrypt'],
176
+ '5min',
177
+ account.address,
178
+ account.chain // 使用账户实际的链 ID
179
+ )
180
+
181
+ // 3. 执行非对称加密生成 signcode
182
+ const timestamp = Date.now().toString()
183
+ const { result, publicKey } = await asymmetricEncrypt(
184
+ tokenId,
185
+ sessionSecret,
186
+ timestamp,
187
+ systemPubKeyHex
188
+ )
189
+
190
+ // 返回 Buffer 格式,与 RWA 后端期望一致
191
+ return {
192
+ address: account.address,
193
+ publicKey: Buffer.from(publicKey, 'hex'),
194
+ signcode: Buffer.from(result, 'hex'),
195
+ }
196
+ }
package/src/index.ts ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @biochain/dweb-compat
3
+ *
4
+ * dweb-plaoc 到 KeyApp bio provider 的兼容适配器
5
+ *
6
+ * 提供与 @nilai/dweb-plaoc 相同的 API 接口,
7
+ * 底层通过 postMessage 调用 KeyApp Bio Provider
8
+ */
9
+
10
+ // 核心功能
11
+ export { getWalleterAddresss, verifyAddressImport } from './address'
12
+ export { getExternalAppData } from './signature'
13
+
14
+ // Crypto Token API
15
+ export {
16
+ requestCryptoToken,
17
+ asymmetricEncrypt,
18
+ signData,
19
+ rwaLogin,
20
+ } from './crypto-token'
21
+ export type {
22
+ CryptoAction,
23
+ TokenDuration,
24
+ RequestCryptoTokenParams,
25
+ RequestCryptoTokenResponse,
26
+ AsymmetricEncryptParams,
27
+ SignParams,
28
+ CryptoExecuteResponse,
29
+ RwaLoginResult,
30
+ } from './crypto-token'
31
+
32
+ // 类型和常量
33
+ export {
34
+ $WALLET_PLAOC_PATH,
35
+ $WALLET_SIGNATURE_TYPE,
36
+ $WALLET_AUTHORIZE_ADDRESS_TYPE,
37
+ } from './types'
38
+ export type {
39
+ $CHAIN_NAME,
40
+ $WALLET_SIGNATURE_TRANSFER,
41
+ $WALLET_SIGNATURE_MESSAGE,
42
+ $WALLET_SIGNATURE_JSON,
43
+ $WALLET_SIGNATURE_DESTORY_ASSET,
44
+ $WALLET_SIGNATURE_PARAMETER,
45
+ $WalletGetAddressResponse,
46
+ $WalletSignatureResponse,
47
+ DwebAppConfig,
48
+ } from './types'
49
+
50
+ // 空操作函数
51
+ export {
52
+ canOpenUrl,
53
+ focusWindow,
54
+ appMaximize,
55
+ appVersionUpdate,
56
+ browserOpen,
57
+ restart,
58
+ getDwebAppDownUrl,
59
+ gotoDwebAppMarketDownLoad,
60
+ CorrecDwebAppLang,
61
+ listenerBackButton,
62
+ dwebAppConfig,
63
+ } from './noop'
package/src/is-dweb.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @plaoc/is-dweb 兼容层
3
+ *
4
+ * 在 miniapp 模式下返回 false
5
+ */
6
+
7
+ /**
8
+ * 判断是否在 dweb 环境
9
+ */
10
+ export function isDweb(): boolean {
11
+ return false
12
+ }
13
+
14
+ /**
15
+ * 获取 dweb 大版本
16
+ */
17
+ export function dwebTarget(): number {
18
+ return 0
19
+ }
20
+
21
+ /**
22
+ * 判断是否移动端
23
+ */
24
+ export function isMobile(): boolean {
25
+ const nav = navigator as Navigator & { userAgentData?: { mobile: boolean } }
26
+ if (typeof navigator !== 'undefined' && nav.userAgentData) {
27
+ return !!nav.userAgentData.mobile
28
+ }
29
+ // 降级到 UA 检测
30
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
31
+ navigator.userAgent
32
+ )
33
+ }
package/src/noop.ts ADDED
@@ -0,0 +1,145 @@
1
+ /**
2
+ * 空操作函数
3
+ *
4
+ * 这些函数在 dweb 环境中有实际功能,但在 miniapp 中不需要
5
+ */
6
+
7
+ import type { DwebAppConfig } from './types'
8
+
9
+ /**
10
+ * 检查应用是否可打开
11
+ *
12
+ * 在 miniapp 模式下总是返回 true,因为钱包功能由宿主提供
13
+ */
14
+ export function canOpenUrl(_mmid: `${string}.dweb`): Promise<{ success: boolean }> {
15
+ return Promise.resolve({ success: true })
16
+ }
17
+
18
+ /**
19
+ * 聚焦窗口
20
+ *
21
+ * 在 miniapp 模式下不需要此操作
22
+ */
23
+ export function focusWindow(): Promise<Response> {
24
+ return Promise.resolve(new Response())
25
+ }
26
+
27
+ /**
28
+ * 窗口最大化
29
+ *
30
+ * 在 miniapp 模式下不需要此操作
31
+ */
32
+ export function appMaximize(): Promise<Response> {
33
+ return Promise.resolve(new Response())
34
+ }
35
+
36
+ /**
37
+ * 版本更新
38
+ *
39
+ * 在 miniapp 模式下不支持
40
+ */
41
+ export function appVersionUpdate(_metadataUrl: string): Window | null {
42
+ console.warn('[dweb-compat] appVersionUpdate is not supported in miniapp mode')
43
+ return null
44
+ }
45
+
46
+ /**
47
+ * 打开浏览器
48
+ *
49
+ * 在 miniapp 模式下使用 window.open
50
+ */
51
+ export function browserOpen(url: string, target?: string): Promise<Window | null> {
52
+ return Promise.resolve(window.open(url, target || '_blank'))
53
+ }
54
+
55
+ /**
56
+ * 重启应用
57
+ *
58
+ * 在 miniapp 模式下刷新页面
59
+ */
60
+ export function restart(): Promise<boolean> {
61
+ window.location.reload()
62
+ return Promise.resolve(true)
63
+ }
64
+
65
+ /**
66
+ * 获取 Dweb App 下载地址
67
+ *
68
+ * 在 miniapp 模式下不需要下载钱包
69
+ */
70
+ export function getDwebAppDownUrl(_info: {
71
+ marketUrl: string
72
+ name: string
73
+ applistJson: string
74
+ }): Promise<{ downloadUrl?: string; marketUrl?: string }> {
75
+ return Promise.resolve({})
76
+ }
77
+
78
+ /**
79
+ * 跳转到应用商城下载
80
+ *
81
+ * 在 miniapp 模式下不需要
82
+ */
83
+ export function gotoDwebAppMarketDownLoad(_info: {
84
+ downloadUrl?: string
85
+ marketUrl?: string
86
+ }): Promise<Window | null> {
87
+ console.warn('[dweb-compat] gotoDwebAppMarketDownLoad is not supported in miniapp mode')
88
+ return Promise.resolve(null)
89
+ }
90
+
91
+ /**
92
+ * 矫正语言
93
+ */
94
+ export function CorrecDwebAppLang(lang?: string): Promise<string> {
95
+ return Promise.resolve(lang || navigator.language)
96
+ }
97
+
98
+ /**
99
+ * 监听硬件返回按钮
100
+ *
101
+ * 在 miniapp 模式下使用浏览器历史 API
102
+ */
103
+ export function listenerBackButton(callback: () => void): void {
104
+ window.addEventListener('popstate', callback)
105
+ }
106
+
107
+ /**
108
+ * DwebApp 配置
109
+ *
110
+ * 在 miniapp 模式下返回空配置
111
+ */
112
+ export const dwebAppConfig = {
113
+ XPay(_alpha = false): DwebAppConfig {
114
+ return {
115
+ mmid: 'pay.nikola-x.com.dweb',
116
+ name: 'X Pay',
117
+ marketUrl: '',
118
+ applistJson: '',
119
+ }
120
+ },
121
+ BIWMeta(_alpha = false): DwebAppConfig {
122
+ return {
123
+ mmid: 'biw-meta.com.dweb',
124
+ name: 'BIWMeta',
125
+ marketUrl: '',
126
+ applistJson: '',
127
+ }
128
+ },
129
+ BFMPay(_alpha = false): DwebAppConfig {
130
+ return {
131
+ mmid: 'pay.bfmeta.info.dweb',
132
+ name: 'BFM Pay',
133
+ marketUrl: '',
134
+ applistJson: '',
135
+ }
136
+ },
137
+ NiLai(_alpha = false): DwebAppConfig {
138
+ return {
139
+ mmid: 'www.ni-lai.com.dweb',
140
+ name: 'NiLai',
141
+ marketUrl: '',
142
+ applistJson: '',
143
+ }
144
+ },
145
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @plaoc/plugins 存根
3
+ *
4
+ * 在 miniapp 模式下提供空实现
5
+ */
6
+
7
+ // 导出空的 dwebServiceWorker
8
+ export const dwebServiceWorker = {
9
+ fetch: () => Promise.reject(new Error('dwebServiceWorker is not available in miniapp mode')),
10
+ has: () => Promise.resolve(false),
11
+ close: () => Promise.resolve(false),
12
+ restart: () => Promise.resolve(false),
13
+ clearCache: () => Promise.resolve(),
14
+ }
15
+
16
+ // 导出空的 windowPlugin
17
+ export const windowPlugin = {
18
+ maximize: () => Promise.resolve(new Response()),
19
+ focusWindow: () => Promise.resolve(new Response()),
20
+ }
21
+
22
+ // 导出空的 configPlugin
23
+ export const configPlugin = {
24
+ getLang: () => Promise.resolve(navigator.language),
25
+ setLang: () => Promise.resolve(true),
26
+ }
27
+
28
+ // 为了兼容性导出类型
29
+ export class WebSocketIpcBuilder {
30
+ constructor(_url: URL, _config: unknown) { }
31
+ get ipc() {
32
+ return {
33
+ request: () => Promise.reject(new Error('WebSocketIpc is not available in miniapp mode')),
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,195 @@
1
+ /**
2
+ * 签名适配
3
+ *
4
+ * 将 dweb 的 getExternalAppData(signature, [...]) 转换为 KeyApp 的 bio_* 方法
5
+ */
6
+
7
+ import { bioRequest } from './bridge'
8
+ import {
9
+ $WALLET_PLAOC_PATH,
10
+ $WALLET_SIGNATURE_TYPE,
11
+ type $WALLET_SIGNATURE_PARAMETER,
12
+ type $WALLET_PLAOC_PATH_RESPONSE,
13
+ } from './types'
14
+
15
+ /** 转账响应 */
16
+ interface TransferResponse {
17
+ txId: string
18
+ transaction: string | object
19
+ }
20
+
21
+ /**
22
+ * 调用外部 App 获取数据
23
+ *
24
+ * 对应 dweb 的 getExternalAppData(mmid, pathname, search)
25
+ */
26
+ export function getExternalAppData<T extends keyof $WALLET_PLAOC_PATH_RESPONSE>(
27
+ _mmid: `${string}.dweb`,
28
+ pathname: T,
29
+ params?: unknown
30
+ ) {
31
+ const dataPromise = executeRequest<$WALLET_PLAOC_PATH_RESPONSE[T]>(pathname, params)
32
+
33
+ return {
34
+ getData: (): Promise<$WALLET_PLAOC_PATH_RESPONSE[T]> => dataPromise,
35
+ abort: (): void => {
36
+ // postMessage 请求不支持中止,这里只是兼容接口
37
+ console.warn('[dweb-compat] abort is not supported in miniapp mode')
38
+ },
39
+ }
40
+ }
41
+
42
+ async function executeRequest<T>(pathname: string, params: unknown): Promise<T> {
43
+ switch (pathname) {
44
+ case $WALLET_PLAOC_PATH.signature:
45
+ if (Array.isArray(params)) {
46
+ const results = await Promise.all(params.map(handleSignatureParam))
47
+ return results as T
48
+ }
49
+ throw new Error('Invalid signature params: expected array')
50
+
51
+ case $WALLET_PLAOC_PATH.getAddress:
52
+ // 地址请求走 address.ts 的 getWalleterAddresss
53
+ throw new Error('Use getWalleterAddresss for address requests')
54
+
55
+ case $WALLET_PLAOC_PATH.stakeAsset:
56
+ // 锻造功能暂不支持
57
+ throw new Error('stakeAsset is not supported in miniapp mode')
58
+
59
+ default:
60
+ throw new Error(`Unknown pathname: ${pathname}`)
61
+ }
62
+ }
63
+
64
+ async function handleSignatureParam(
65
+ param: $WALLET_SIGNATURE_PARAMETER
66
+ ): Promise<TransferResponse | string | object | null> {
67
+ switch (param.type) {
68
+ case $WALLET_SIGNATURE_TYPE.transfer:
69
+ return handleTransfer(param)
70
+
71
+ case $WALLET_SIGNATURE_TYPE.message:
72
+ return handleMessage(param)
73
+
74
+ case $WALLET_SIGNATURE_TYPE.json:
75
+ return handleJsonSign(param)
76
+
77
+ case $WALLET_SIGNATURE_TYPE.destory:
78
+ return handleDestroy(param)
79
+
80
+ case $WALLET_SIGNATURE_TYPE.assetTypeBalance:
81
+ return handleGetBalance(param)
82
+
83
+ case $WALLET_SIGNATURE_TYPE.contract:
84
+ return handleContract(param)
85
+
86
+ default:
87
+ console.error('[dweb-compat] Unsupported signature type:', (param as { type: number }).type)
88
+ return { error: true, message: `Unsupported signature type: ${(param as { type: number }).type}` }
89
+ }
90
+ }
91
+
92
+ async function handleTransfer(param: {
93
+ chainName: string
94
+ senderAddress: string
95
+ receiveAddress: string
96
+ balance: string
97
+ assetType?: string
98
+ contractInfo?: { contractAddress: string; assetType: string; decimals: number | string }
99
+ }): Promise<TransferResponse> {
100
+ const result = await bioRequest<{ txHash: string; transaction?: object }>('bio_sendTransaction', {
101
+ from: param.senderAddress,
102
+ to: param.receiveAddress,
103
+ amount: param.balance,
104
+ chain: param.chainName,
105
+ asset: param.assetType,
106
+ contractInfo: param.contractInfo,
107
+ })
108
+
109
+ return {
110
+ txId: result.txHash,
111
+ transaction: result.transaction || result.txHash,
112
+ }
113
+ }
114
+
115
+ async function handleMessage(param: {
116
+ chainName: string
117
+ senderAddress: string
118
+ message: string
119
+ }): Promise<string> {
120
+ const signature = await bioRequest<string>('bio_signMessage', {
121
+ address: param.senderAddress,
122
+ message: param.message,
123
+ chain: param.chainName,
124
+ })
125
+ return signature
126
+ }
127
+
128
+ async function handleJsonSign(param: {
129
+ chainName: string
130
+ senderAddress: string
131
+ json: object
132
+ jsonInterpolation?: Array<{ path: string; index: 0 | 1 }>
133
+ }): Promise<TransferResponse> {
134
+ const result = await bioRequest<{ txId: string; transaction: object }>('bio_signTypedData', {
135
+ address: param.senderAddress,
136
+ data: param.json,
137
+ chain: param.chainName,
138
+ interpolation: param.jsonInterpolation,
139
+ })
140
+ return result
141
+ }
142
+
143
+ async function handleDestroy(param: {
144
+ chainName: string
145
+ senderAddress: string
146
+ assetType: string
147
+ destoryAmount: string
148
+ }): Promise<TransferResponse> {
149
+ const result = await bioRequest<{ txId: string; transaction: object }>('bio_destroyAsset', {
150
+ address: param.senderAddress,
151
+ chain: param.chainName,
152
+ assetType: param.assetType,
153
+ amount: param.destoryAmount,
154
+ })
155
+ return result
156
+ }
157
+
158
+ async function handleGetBalance(param: {
159
+ chainName: string
160
+ senderAddress: string
161
+ assetTypes: Array<{ assetType: string; contractAddress?: string }>
162
+ }): Promise<Record<string, { assetType: string; decimals: number; balance: string }>> {
163
+ const result = await bioRequest<Record<string, { assetType: string; decimals: number; balance: string }>>(
164
+ 'bio_getBalance',
165
+ {
166
+ address: param.senderAddress,
167
+ chain: param.chainName,
168
+ assets: param.assetTypes,
169
+ }
170
+ )
171
+ return result
172
+ }
173
+
174
+ async function handleContract(param: {
175
+ chainName: string
176
+ senderAddress: string
177
+ receiveAddress: string
178
+ methods: string
179
+ params: unknown[]
180
+ data: string
181
+ amount?: string
182
+ }): Promise<TransferResponse> {
183
+ // 合约调用通过 bio_signTransaction
184
+ const result = await bioRequest<{ txId: string; signedTx: object }>('bio_signTransaction', {
185
+ from: param.senderAddress,
186
+ to: param.receiveAddress,
187
+ chain: param.chainName,
188
+ data: param.data,
189
+ value: param.amount || '0',
190
+ })
191
+ return {
192
+ txId: result.txId,
193
+ transaction: result.signedTx,
194
+ }
195
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Tauri Plugin Opener Stub
3
+ *
4
+ * 在 miniapp 模式下替代 @tauri-apps/plugin-opener
5
+ */
6
+
7
+ /**
8
+ * 在浏览器中打开 URL
9
+ */
10
+ export async function openUrl(url: string): Promise<void> {
11
+ window.open(url, '_blank')
12
+ }
package/src/types.ts ADDED
@@ -0,0 +1,187 @@
1
+ /**
2
+ * dweb-plaoc 类型定义
3
+ *
4
+ * 从 @nilai/dweb-plaoc/types.ts 复制并简化
5
+ */
6
+
7
+ /** 链名称类型 */
8
+ export type $CHAIN_NAME =
9
+ | 'BFMeta'
10
+ | 'BFMetaV2'
11
+ | 'BIWMeta'
12
+ | 'Ethereum'
13
+ | 'Binance'
14
+ | 'Tron'
15
+
16
+ /** API 路径 */
17
+ export enum $WALLET_PLAOC_PATH {
18
+ /** 获取地址 */
19
+ getAddress = '/wallet/authorize/address',
20
+ /** 签名 */
21
+ signature = '/wallet/authorize/signature',
22
+ /** 锻造 */
23
+ stakeAsset = '/wallet/staking',
24
+ }
25
+
26
+ /** 地址授权类型 */
27
+ export enum $WALLET_AUTHORIZE_ADDRESS_TYPE {
28
+ main = 'main',
29
+ network = 'network',
30
+ all = 'all',
31
+ }
32
+
33
+ /** 签名类型 */
34
+ export enum $WALLET_SIGNATURE_TYPE {
35
+ /** 消息签名 */
36
+ message = 0,
37
+ /** 转账 */
38
+ transfer = 1,
39
+ /** 内链:凭证资产转移 */
40
+ bioforestChainCertificateTransfer = 2,
41
+ /** JSON 签名 */
42
+ json = 3,
43
+ /** 内链:同质化(非同质化)交易 */
44
+ ENTITY = 4,
45
+ /** 获取币种余额 */
46
+ assetTypeBalance = 5,
47
+ /** 调用智能合约 */
48
+ contract = 6,
49
+ /** 内链销毁 */
50
+ destory = 7,
51
+ }
52
+
53
+ /** 消息签名参数 */
54
+ export interface $WALLET_SIGNATURE_MESSAGE {
55
+ type: typeof $WALLET_SIGNATURE_TYPE.message
56
+ chainName: $CHAIN_NAME
57
+ senderAddress: string
58
+ message: string
59
+ }
60
+
61
+ /** 转账签名参数 */
62
+ export interface $WALLET_SIGNATURE_TRANSFER {
63
+ type: typeof $WALLET_SIGNATURE_TYPE.transfer
64
+ chainName: $CHAIN_NAME
65
+ assetType?: string
66
+ senderAddress: string
67
+ receiveAddress: string
68
+ privateKey?: string
69
+ balance: string
70
+ fee?: string
71
+ contractInfo?: {
72
+ contractAddress: string
73
+ assetType: string
74
+ decimals: number | string
75
+ }
76
+ remark?: Record<string, string>
77
+ }
78
+
79
+ /** JSON 签名参数 */
80
+ export interface $WALLET_SIGNATURE_JSON {
81
+ type: typeof $WALLET_SIGNATURE_TYPE.json
82
+ chainName: $CHAIN_NAME
83
+ senderAddress: string
84
+ json: object
85
+ jsonInterpolation?: Array<{
86
+ path: string
87
+ index: 0 | 1
88
+ }>
89
+ }
90
+
91
+ /** 销毁资产参数 */
92
+ export interface $WALLET_SIGNATURE_DESTORY_ASSET {
93
+ type: typeof $WALLET_SIGNATURE_TYPE.destory
94
+ chainName: $CHAIN_NAME
95
+ assetType: string
96
+ fee?: string
97
+ senderAddress: string
98
+ destoryAddress?: string
99
+ destoryAmount: string
100
+ remark?: Record<string, string>
101
+ }
102
+
103
+ /** 获取余额参数 */
104
+ export interface $WALLET_ASSETTYPE_BALANCE {
105
+ type: typeof $WALLET_SIGNATURE_TYPE.assetTypeBalance
106
+ chainName: $CHAIN_NAME
107
+ senderAddress: string
108
+ assetTypes: Array<{
109
+ assetType: string
110
+ contractAddress?: string
111
+ }>
112
+ }
113
+
114
+ /** 合约调用参数 */
115
+ export interface $WALLET_SIGNATURE_CONTRACT {
116
+ type: typeof $WALLET_SIGNATURE_TYPE.contract
117
+ chainName: $CHAIN_NAME
118
+ amount?: string
119
+ methods: string
120
+ params: unknown[]
121
+ senderAddress: string
122
+ receiveAddress: string
123
+ data: string
124
+ fee?: string
125
+ binanceGasInfo?: {
126
+ gasLimit: number | string
127
+ gasPrice: number | string
128
+ }
129
+ boradcast?: boolean
130
+ waitTrsInBlock?: boolean
131
+ waitTime?: number
132
+ }
133
+
134
+ /** 签名参数联合类型 */
135
+ export type $WALLET_SIGNATURE_PARAMETER =
136
+ | $WALLET_SIGNATURE_MESSAGE
137
+ | $WALLET_SIGNATURE_DESTORY_ASSET
138
+ | $WALLET_SIGNATURE_TRANSFER
139
+ | $WALLET_SIGNATURE_JSON
140
+ | $WALLET_ASSETTYPE_BALANCE
141
+ | $WALLET_SIGNATURE_CONTRACT
142
+
143
+ /** 地址响应 */
144
+ export type $WalletGetAddressResponse = Array<{
145
+ name: string
146
+ chainName: $CHAIN_NAME
147
+ address: string
148
+ main: string
149
+ publicKey: string
150
+ privateKey: string
151
+ magic: string
152
+ signMessage?: string
153
+ }>
154
+
155
+ /** 签名响应 */
156
+ export type $WalletSignatureResponse = Array<
157
+ | string
158
+ | object
159
+ | { txId: string; transaction: string | object }
160
+ | { [assetType: string]: { assetType: string; decimals: number; balance: string; contracts?: string } }
161
+ | null
162
+ | { error: boolean; message: string }
163
+ >
164
+
165
+ /** API 响应类型映射 */
166
+ export type $WALLET_PLAOC_PATH_RESPONSE = {
167
+ [$WALLET_PLAOC_PATH.getAddress]: $WalletGetAddressResponse | null
168
+ [$WALLET_PLAOC_PATH.signature]: $WalletSignatureResponse | null
169
+ [$WALLET_PLAOC_PATH.stakeAsset]: {
170
+ address: string
171
+ orderId: string
172
+ stakeAssetType: string
173
+ stakeChainName: $CHAIN_NAME
174
+ stakeAmount: string
175
+ mintAssetType: string
176
+ mintChainName: $CHAIN_NAME
177
+ mintAmount: string
178
+ } | null
179
+ }
180
+
181
+ /** DwebApp 配置 */
182
+ export interface DwebAppConfig {
183
+ mmid: `${string}.dweb`
184
+ name: string
185
+ marketUrl: string
186
+ applistJson: string
187
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../../tsconfig.app.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "outDir": "./dist"
6
+ },
7
+ "include": [
8
+ "src/**/*"
9
+ ],
10
+ "exclude": []
11
+ }