Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions miniapps/biobridge/src/hooks/useForge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ export function useForge() {
params: [{
message: messageToSign,
address: internalAccount.address,
chainName: internalChain,
}],
})

Expand Down
2 changes: 1 addition & 1 deletion packages/dweb-compat/src/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export async function verifyAddressImport(
const signature = await bioRequest<string>('bio_signMessage', {
address: opts.address,
message: opts.message,
chain: opts.chainName,
chainName: opts.chainName,
})
return typeof signature === 'string' && signature.length > 0
} catch {
Expand Down
4 changes: 2 additions & 2 deletions packages/dweb-compat/src/signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ async function handleMessage(param: {
const signature = await bioRequest<string>('bio_signMessage', {
address: param.senderAddress,
message: param.message,
chain: param.chainName,
chainName: param.chainName,
})
return signature
}
Expand All @@ -134,7 +134,7 @@ async function handleJsonSign(param: {
const result = await bioRequest<{ txId: string; transaction: object }>('bio_signTypedData', {
address: param.senderAddress,
data: param.json,
chain: param.chainName,
chainName: param.chainName,
interpolation: param.jsonInterpolation,
})
return result
Expand Down
2 changes: 1 addition & 1 deletion packages/flow/tsconfig.tsbuildinfo

Large diffs are not rendered by default.

24 changes: 21 additions & 3 deletions src/services/authorize/signature-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@ import type { DestroyPayload, MessagePayload, TransferPayload } from './types'
import { WALLET_PLAOC_PATH } from './paths'
import { sha256 } from '@noble/hashes/sha2.js'
import { bytesToHex } from '@noble/hashes/utils.js'
import { createBioforestKeypair, decrypt, isBioforestChain, signMessage, verifyPassword, type EncryptedData } from '@/lib/crypto'
import {
createBioforestKeypair,
decrypt,
isBioforestChain,
publicKeyToBioforestAddress,
signMessage,
verifyPassword,
type EncryptedData,
} from '@/lib/crypto'
import { chainConfigService } from '@/services/chain-config/service'

export type SignatureAuthError = 'rejected' | 'timeout' | 'insufficient_balance'

Expand Down Expand Up @@ -51,9 +60,13 @@ export class SignatureAuthService {
* NOTE: This method does not call `approve()` automatically to keep UI control explicit.
*/
async handleMessageSign(payload: MessagePayload, encryptedSecret: EncryptedData, password: string): Promise<SignatureResult> {
const chainName = payload.chainName.trim().toLowerCase()
const rawChainName = payload.chainName.trim()
const normalizedChainName = rawChainName.toLowerCase()
const chainConfig =
chainConfigService.getConfig(rawChainName) ?? chainConfigService.getConfig(normalizedChainName)
const isBioforest = isBioforestChain(normalizedChainName) || chainConfig?.chainKind === 'bioforest'

if (isBioforestChain(chainName)) {
if (isBioforest) {
let secret: string
try {
secret = await decrypt(encryptedSecret, password)
Expand All @@ -62,6 +75,11 @@ export class SignatureAuthService {
}

const keypair = createBioforestKeypair(secret)
const prefix = chainConfig?.prefix ?? payload.senderAddress.slice(0, 1) || 'b'
const derivedAddress = publicKeyToBioforestAddress(keypair.publicKey, prefix)
if (derivedAddress !== payload.senderAddress) {
throw new Error('Signing address mismatch')
}
const signature = signMessage(payload.message, keypair.secretKey)
return {
signature: `0x${bytesToHex(signature)}`,
Expand Down
1 change: 1 addition & 0 deletions src/services/ecosystem/handlers/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export interface MiniappInfo {
export interface SigningParams {
message: string
address: string
chainName: string
app: MiniappInfo
}

Expand Down
14 changes: 8 additions & 6 deletions src/services/ecosystem/handlers/signing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ function getSigningDialog(appId: string) {

/** bio_signMessage - Sign a message, returns { signature, publicKey } */
export const handleSignMessage: MethodHandler = async (params, context) => {
const opts = params as { message?: string; address?: string } | undefined
if (!opts?.message || !opts?.address) {
throw Object.assign(new Error('Missing message or address'), { code: BioErrorCodes.INVALID_PARAMS })
const opts = params as { message?: string; address?: string; chainName?: string } | undefined
if (!opts?.message || !opts?.address || !opts?.chainName) {
throw Object.assign(new Error('Missing message, address, or chainName'), { code: BioErrorCodes.INVALID_PARAMS })
}

const showSigningDialog = getSigningDialog(context.appId)
Expand All @@ -35,6 +35,7 @@ export const handleSignMessage: MethodHandler = async (params, context) => {
const result = await showSigningDialog({
message: opts.message,
address: opts.address,
chainName: opts.chainName,
app: { name: context.appName },
})

Expand All @@ -48,9 +49,9 @@ export const handleSignMessage: MethodHandler = async (params, context) => {

/** bio_signTypedData - Sign typed data, returns { signature, publicKey } */
export const handleSignTypedData: MethodHandler = async (params, context) => {
const opts = params as { data?: object; address?: string } | undefined
if (!opts?.data || !opts?.address) {
throw Object.assign(new Error('Missing data or address'), { code: BioErrorCodes.INVALID_PARAMS })
const opts = params as { data?: object; address?: string; chainName?: string } | undefined
if (!opts?.data || !opts?.address || !opts?.chainName) {
throw Object.assign(new Error('Missing data, address, or chainName'), { code: BioErrorCodes.INVALID_PARAMS })
}

const showSigningDialog = getSigningDialog(context.appId)
Expand All @@ -64,6 +65,7 @@ export const handleSignTypedData: MethodHandler = async (params, context) => {
const result = await showSigningDialog({
message,
address: opts.address,
chainName: opts.chainName,
app: { name: context.appName },
})

Expand Down
6 changes: 1 addition & 5 deletions src/stackflow/activities/MainTabsActivity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,6 @@ export const MainTabsActivity: ActivityComponentType<MainTabsParams> = ({ params
});

setSigningDialog(async (params) => {
const resolvedChain = walletStore.state.wallets
.flatMap((wallet) => wallet.chainAddresses)
.find((ca) => ca.address === params.address)?.chain

return new Promise<{ signature: string; publicKey: string } | null>((resolve) => {
const timeout = window.setTimeout(() => resolve(null), 60_000);

Expand All @@ -136,7 +132,7 @@ export const MainTabsActivity: ActivityComponentType<MainTabsParams> = ({ params
address: params.address,
appName: params.app.name,
appIcon: params.app.icon ?? "",
chainName: resolvedChain ?? "bioforest",
chainName: params.chainName,
});
});
});
Expand Down
20 changes: 14 additions & 6 deletions src/stackflow/activities/sheets/SigningConfirmJob.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ type SigningConfirmJobParams = {
message: string;
/** 签名地址 */
address: string;
/** 签名链 */
chainName: string;
/** 请求来源小程序名称 */
appName?: string;
/** 请求来源小程序图标 */
appIcon?: string;
/** 链名称(用于签名) */
chainName?: string;
};

function SigningConfirmJobContent() {
Expand All @@ -37,7 +37,15 @@ function SigningConfirmJobContent() {
const [isSubmitting, setIsSubmitting] = useState(false);

// 查找使用该地址的钱包
const targetWallet = walletStore.state.wallets.find((w) => w.chainAddresses.some((ca) => ca.address === address));
const normalizedChainName = chainName.trim().toLowerCase();
const targetWallet = walletStore.state.wallets.find((w) =>
w.chainAddresses.some(
(ca) => ca.address === address && ca.chain.toLowerCase() === normalizedChainName,
),
);
const resolvedChainId = targetWallet?.chainAddresses.find(
(ca) => ca.address === address && ca.chain.toLowerCase() === normalizedChainName,
)?.chain;
const walletName = targetWallet?.name || t('unknownWallet');

const handleConfirm = useCallback(() => {
Expand All @@ -49,7 +57,7 @@ function SigningConfirmJobContent() {

try {
const encryptedSecret = targetWallet?.encryptedMnemonic ?? currentWallet?.encryptedMnemonic;
if (!encryptedSecret || !targetWallet) {
if (!encryptedSecret || !targetWallet || !resolvedChainId) {
throw new Error(t('signingAddressNotFound'));
}

Expand All @@ -60,7 +68,7 @@ function SigningConfirmJobContent() {
// 执行真实签名(返回 { signature, publicKey })
const signResult = await authService.handleMessageSign(
{
chainName: chainName || 'bioforest',
chainName: resolvedChainId,
senderAddress: address,
message,
},
Expand Down Expand Up @@ -121,7 +129,7 @@ function SigningConfirmJobContent() {
walletInfo={{
name: walletName,
address,
chainId: chainName || 'bfmeta',
chainId: resolvedChainId ?? 'bfmeta',
}}
/>

Expand Down