diff --git a/package.json b/package.json index 022b613..a15f007 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,8 @@ "@flowindex/evm-wallet": "^0.1.0", "@flowindex/flow-passkey": "latest", "@flowindex/flow-signer": "latest", + "@noble/curves": "^2.0.1", + "@noble/hashes": "^2.0.1", "@onflow/fcl": "^1.21.9", "@onflow/rlp": "^1.2.4", "@radix-ui/react-avatar": "^1.1.11", diff --git a/pages/_app.tsx b/pages/_app.tsx index f90babd..8e40b12 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -41,8 +41,10 @@ async function applyUrlParams(store: Record, setStore: (s: Record, setStore: (s: Record, setStore: (s: Record 0) { const acct = accounts[0]; updated.address = acct.address; diff --git a/tsconfig.json b/tsconfig.json index 6b44d53..810c8c5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,7 @@ "incremental": true, "esModuleInterop": true, "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "react-jsx", diff --git a/utils/sign.ts b/utils/sign.ts index df82d19..259b4fe 100644 --- a/utils/sign.ts +++ b/utils/sign.ts @@ -1,6 +1,5 @@ import { signFlowTransaction } from "@flowindex/flow-passkey"; -import { LocalSigner } from "@flowindex/flow-signer"; -import { HASH_ALGO, KEY_TYPE, SIGN_ALGO } from "./constants"; +import { KEY_TYPE } from "./constants"; interface SignStore { id?: string; @@ -36,6 +35,37 @@ const domainTag = (tag: string): string => { ).toString("hex") } +/** + * Sign a hex message with a raw private key using ECDSA. + * Uses @noble/hashes for hashing and @noble/curves for signing — + * both are pure JS with no Web Crypto or network dependency. + * Supports ECDSA_P256 and ECDSA_secp256k1 with SHA2_256 or SHA3_256. + */ +async function signEcdsa(pk: string, message: string, sigAlgo: string, hashAlgo: string): Promise { + const msgBytes = Buffer.from(message, "hex"); + + let hashBytes: Uint8Array; + if (hashAlgo === "SHA3_256") { + const { sha3_256 } = await import("@noble/hashes/sha3.js"); + hashBytes = sha3_256(msgBytes); + } else { + const { sha256 } = await import("@noble/hashes/sha2.js"); + hashBytes = sha256(msgBytes); + } + + const pkBytes = Uint8Array.from(Buffer.from(pk, "hex")); + let sigBytes: Uint8Array; + if (sigAlgo === "ECDSA_secp256k1") { + const { secp256k1 } = await import("@noble/curves/secp256k1.js"); + sigBytes = secp256k1.sign(hashBytes, pkBytes); + } else { + const { p256 } = await import("@noble/curves/nist.js"); + sigBytes = p256.sign(hashBytes, pkBytes); + } + // sign() returns compact r||s (64 bytes) — convert directly to hex + return Buffer.from(sigBytes).toString("hex"); +} + /** * Sign a hex-encoded message. Returns { signature, extensionData? }. * - Passkey: returns extensionData for FLIP-264 @@ -47,20 +77,10 @@ const signWithKey = async (store: SignStore, message: string): Promise