@notabene/verify-proof 1.8.1 → 1.11.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 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notabene/verify-proof",
3
- "version": "1.8.1",
3
+ "version": "1.11.0",
4
4
  "description": "Verify ownership proofs",
5
5
  "source": "src/index.ts",
6
6
  "type": "module",
@@ -44,8 +44,9 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@cardano-foundation/cardano-verify-datasignature": "^1.0.11",
47
+ "@cosmjs/amino": "^0.34.0",
47
48
  "@noble/curves": "^1.7.0",
48
- "@notabene/javascript-sdk": "^2.11.0",
49
+ "@notabene/javascript-sdk": "2.11.0-next.2",
49
50
  "@scure/base": "^1.2.1",
50
51
  "@stellar/stellar-sdk": "^13.1.0",
51
52
  "bip322-js": "^2.0.0",
package/src/bitcoin.ts CHANGED
@@ -48,23 +48,23 @@ const CHAIN_CONFIGS: Record<string, ChainConfig> = {
48
48
  },
49
49
  dogecoin: {
50
50
  messagePrefix: "\u0019Dogecoin Signed Message:\n",
51
- pubKeyHashVersion: 0x1E, // D...
51
+ pubKeyHashVersion: 0x1e, // D...
52
52
  scriptHashVersion: 0x16, // A...
53
53
  isTestnet: false,
54
54
  },
55
55
  dash: {
56
56
  messagePrefix: "\u0019DarkCoin Signed Message:\n",
57
- pubKeyHashVersion: 0x4C, // X...
57
+ pubKeyHashVersion: 0x4c, // X...
58
58
  scriptHashVersion: 0x10, // 7...
59
59
  isTestnet: false,
60
60
  },
61
61
  zcash: {
62
62
  messagePrefix: "\u0018Zcash Signed Message:\n",
63
- pubKeyHashVersion: Uint8Array.from([0x1C, 0xB8]), // <-- FIXED
64
- scriptHashVersion: Uint8Array.from([0x1C, 0xBD]),
63
+ pubKeyHashVersion: Uint8Array.from([0x1c, 0xb8]), // <-- FIXED
64
+ scriptHashVersion: Uint8Array.from([0x1c, 0xbd]),
65
65
  isTestnet: false,
66
66
  },
67
-
67
+
68
68
  testnet: {
69
69
  messagePrefix: "\u0018Bitcoin Signed Message:\n",
70
70
  pubKeyHashVersion: 0x6f, // m or n
@@ -88,9 +88,9 @@ enum DerivationMode {
88
88
  export async function verifyBTCSignature(
89
89
  proof: SignatureProof
90
90
  ): Promise<SignatureProof> {
91
- const [ns,, address] = proof.address.split(/:/);
91
+ const [ns, , address] = proof.address.split(/:/);
92
92
  if (ns !== "bip122") return { ...proof, status: ProofStatus.FAILED };
93
-
93
+
94
94
  // Map chainId to our chain configuration
95
95
  const chainConfig = getChainConfig(address);
96
96
  if (!chainConfig) return { ...proof, status: ProofStatus.FAILED };
@@ -104,7 +104,16 @@ export async function verifyBTCSignature(
104
104
  if (chainConfig.isTestnet) {
105
105
  return verifyBIP322(address, proof);
106
106
  }
107
-
107
+
108
+ // Check if this is a Taproot address (bc1p or tb1p)
109
+ const isTaproot = address.startsWith("bc1p") || address.startsWith("tb1p");
110
+
111
+ // For Taproot addresses with BIP-137 proof type, use BIP-322 verification
112
+ // since BIP-137 doesn't officially support Taproot
113
+ if (isTaproot && proof.type === ProofTypes.BIP137) {
114
+ return verifyBIP322(address, proof);
115
+ }
116
+
108
117
  try {
109
118
  switch (proof.type) {
110
119
  case ProofTypes.BIP137:
@@ -117,24 +126,30 @@ export async function verifyBTCSignature(
117
126
  status: ProofStatus.FAILED,
118
127
  };
119
128
  }
120
- } catch (error) {
121
- console.error("error verifying proof", error);
129
+ } catch {
122
130
  return {
123
131
  ...proof,
124
132
  status: ProofStatus.FAILED,
125
- // error: error.message || error,
126
133
  };
127
134
  }
128
135
  }
129
136
 
130
137
  function getChainConfig(address: string): ChainConfig {
131
- if (address.startsWith("1") || address.startsWith("3") || address.startsWith("bc1")) {
138
+ if (
139
+ address.startsWith("1") ||
140
+ address.startsWith("3") ||
141
+ address.startsWith("bc1")
142
+ ) {
132
143
  return CHAIN_CONFIGS["bitcoin"];
133
144
  }
134
145
  if (address.startsWith("t1") || address.startsWith("t3")) {
135
146
  return CHAIN_CONFIGS["zcash"];
136
147
  }
137
- if (address.startsWith("L") || address.startsWith("M") || address.startsWith("ltc1")) {
148
+ if (
149
+ address.startsWith("L") ||
150
+ address.startsWith("M") ||
151
+ address.startsWith("ltc1")
152
+ ) {
138
153
  return CHAIN_CONFIGS["litecoin"];
139
154
  }
140
155
  if (address.startsWith("D") || address.startsWith("A")) {
@@ -153,7 +168,6 @@ function getChainConfig(address: string): ChainConfig {
153
168
  return CHAIN_CONFIGS["bitcoin"];
154
169
  }
155
170
 
156
-
157
171
  function verifyBIP322(address: string, proof: SignatureProof) {
158
172
  const { attestation, proof: signatureProof } = proof;
159
173
  const verified = Verifier.verifySignature(
@@ -167,17 +181,29 @@ function verifyBIP322(address: string, proof: SignatureProof) {
167
181
  };
168
182
  }
169
183
 
170
- function verifyBIP137(address: string, proof: SignatureProof, chainConfig: ChainConfig) {
184
+ function verifyBIP137(
185
+ address: string,
186
+ proof: SignatureProof,
187
+ chainConfig: ChainConfig
188
+ ) {
171
189
  const derivationMode = getDerivationMode(address);
172
-
190
+
173
191
  // For legacy addresses (starting with "1"), never use SegWit encoding
174
192
  // For P2SH addresses (starting with "3"), use SegWit encoding if they have bech32 support
175
193
  // For native SegWit addresses (bc1, tb1, ltc1), always use SegWit encoding
176
- const useSegwitEncoding = Boolean(chainConfig.bech32Prefix &&
177
- (derivationMode === DerivationMode.NATIVE ||
178
- (derivationMode === DerivationMode.SEGWIT && !address.startsWith("1"))));
179
-
180
- const verified = verify(proof.attestation, address, proof.proof, useSegwitEncoding, chainConfig);
194
+ const useSegwitEncoding = Boolean(
195
+ chainConfig.bech32Prefix &&
196
+ (derivationMode === DerivationMode.NATIVE ||
197
+ (derivationMode === DerivationMode.SEGWIT && !address.startsWith("1")))
198
+ );
199
+
200
+ const verified = verify(
201
+ proof.attestation,
202
+ address,
203
+ proof.proof,
204
+ useSegwitEncoding,
205
+ chainConfig
206
+ );
181
207
 
182
208
  return {
183
209
  ...proof,
@@ -257,25 +283,34 @@ function verify(
257
283
  let actual: string = "";
258
284
 
259
285
  // Special handling for Bitcoin Cash addresses
260
- if (address.startsWith('q')) {
286
+ if (address.startsWith("q")) {
261
287
  // For BCH, we'll compare the public key hash directly since we're getting a CashAddr
262
288
  // Convert the CashAddr to legacy format for comparison
263
- actual = encodeBase58AddressFormat(chainConfig.pubKeyHashVersion, publicKeyHash);
289
+ actual = encodeBase58AddressFormat(
290
+ chainConfig.pubKeyHashVersion,
291
+ publicKeyHash
292
+ );
264
293
  // Legacy P2PKH addresses in BCH start with '1' just like BTC
265
294
  // Source: https://reference.cash/protocol/blockchain/encoding/cashaddr#legacy-address-format
266
- return actual.startsWith('1');
295
+ return actual.startsWith("1");
267
296
  }
268
297
 
269
298
  if (segwitType) {
270
299
  if (segwitType === SEGWIT_TYPES.P2SH_P2WPKH) {
271
- actual = encodeBase58AddressFormat(chainConfig.scriptHashVersion, publicKeyHash);
300
+ actual = encodeBase58AddressFormat(
301
+ chainConfig.scriptHashVersion,
302
+ publicKeyHash
303
+ );
272
304
  } else {
273
305
  // parsed.segwitType === SEGWIT_TYPES.P2WPKH
274
306
  if (chainConfig.bech32Prefix) {
275
307
  actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);
276
308
  } else {
277
309
  // Fallback to legacy if bech32 not supported
278
- actual = encodeBase58AddressFormat(chainConfig.scriptHashVersion, publicKeyHash);
310
+ actual = encodeBase58AddressFormat(
311
+ chainConfig.scriptHashVersion,
312
+ publicKeyHash
313
+ );
279
314
  // base58 can be p2pkh or p2sh-p2wpkh
280
315
  }
281
316
  }
@@ -288,24 +323,47 @@ function verify(
288
323
  redeemScript[1] = 0x14; // push 20 bytes
289
324
  redeemScript.set(publicKeyHash, 2);
290
325
  const redeemScriptHash = hash160(redeemScript);
291
- const p2shP2wpkh = encodeBase58AddressFormat(chainConfig.scriptHashVersion, redeemScriptHash);
326
+ const p2shP2wpkh = encodeBase58AddressFormat(
327
+ chainConfig.scriptHashVersion,
328
+ redeemScriptHash
329
+ );
292
330
  // Legacy P2SH: script hash of the public key
293
- const legacyP2sh = encodeBase58AddressFormat(chainConfig.scriptHashVersion, publicKeyHash);
331
+ const legacyP2sh = encodeBase58AddressFormat(
332
+ chainConfig.scriptHashVersion,
333
+ publicKeyHash
334
+ );
294
335
  if (address === p2shP2wpkh || address === legacyP2sh) {
295
336
  return true;
296
337
  }
297
338
  actual = legacyP2sh; // fallback for error reporting
339
+ } else if (address.startsWith("bc1q") || address.startsWith("tb1q") || address.startsWith("ltc1q")) {
340
+ // For native SegWit P2WPKH addresses (bc1q/tb1q/ltc1q), always encode as bech32
341
+ // This handles Ledger wallets that sign without segwit flags
342
+ if (chainConfig.bech32Prefix) {
343
+ actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);
344
+ } else {
345
+ actual = encodeBase58AddressFormat(
346
+ chainConfig.pubKeyHashVersion,
347
+ publicKeyHash
348
+ );
349
+ }
298
350
  } else if (checkSegwitAlways && chainConfig.bech32Prefix) {
299
351
  try {
300
352
  actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);
301
353
  // if address is bech32 it is not p2sh
302
354
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
303
355
  } catch (e) {
304
- actual = encodeBase58AddressFormat(chainConfig.scriptHashVersion, publicKeyHash);
356
+ actual = encodeBase58AddressFormat(
357
+ chainConfig.scriptHashVersion,
358
+ publicKeyHash
359
+ );
305
360
  // base58 can be p2pkh or p2sh-p2wpkh
306
361
  }
307
362
  } else {
308
- actual = encodeBase58AddressFormat(chainConfig.pubKeyHashVersion, publicKeyHash);
363
+ actual = encodeBase58AddressFormat(
364
+ chainConfig.pubKeyHashVersion,
365
+ publicKeyHash
366
+ );
309
367
  }
310
368
  }
311
369
 
@@ -314,11 +372,12 @@ function verify(
314
372
 
315
373
  const base58check = createBase58check(Hash.sha256);
316
374
 
317
- function encodeBase58AddressFormat(version: number | Uint8Array, publicKeyHash: Uint8Array) {
375
+ function encodeBase58AddressFormat(
376
+ version: number | Uint8Array,
377
+ publicKeyHash: Uint8Array
378
+ ) {
318
379
  const prefixBytes =
319
- typeof version === "number"
320
- ? Uint8Array.of(version)
321
- : version; // Accept raw Uint8Array for Zcash
380
+ typeof version === "number" ? Uint8Array.of(version) : version; // Accept raw Uint8Array for Zcash
322
381
 
323
382
  const payload = new Uint8Array(prefixBytes.length + publicKeyHash.length);
324
383
  payload.set(prefixBytes);
@@ -326,7 +385,6 @@ function encodeBase58AddressFormat(version: number | Uint8Array, publicKeyHash:
326
385
  return base58check.encode(payload);
327
386
  }
328
387
 
329
-
330
388
  function magicHash(attestation: string, messagePrefix: string) {
331
389
  const prefix = new TextEncoder().encode(messagePrefix);
332
390
  const message = new TextEncoder().encode(attestation);
@@ -340,7 +398,10 @@ function magicHash(attestation: string, messagePrefix: string) {
340
398
  return hash256(buffer);
341
399
  }
342
400
 
343
- function encodeBech32Address(publicKeyHash: Uint8Array, prefix: string = "bc"): string {
401
+ function encodeBech32Address(
402
+ publicKeyHash: Uint8Array,
403
+ prefix: string = "bc"
404
+ ): string {
344
405
  const bwords = bech32.toWords(publicKeyHash);
345
406
  bwords.unshift(0);
346
407
  return bech32.encode(prefix, bwords);
@@ -352,4 +413,4 @@ function hash256(buffer: Uint8Array): Uint8Array {
352
413
 
353
414
  function hash160(buffer: Uint8Array): Uint8Array {
354
415
  return Hash.ripemd160(Hash.sha256(buffer));
355
- }
416
+ }
package/src/cosmos.ts ADDED
@@ -0,0 +1,88 @@
1
+ import {
2
+ CosmosMetadata,
3
+ ProofStatus,
4
+ SignatureProof,
5
+ } from "@notabene/javascript-sdk";
6
+ import { bech32 } from "bech32";
7
+ import { StdSignDoc, serializeSignDoc } from "@cosmjs/amino";
8
+ import { secp256k1 } from "@noble/curves/secp256k1";
9
+ import { ripemd160 } from "@noble/hashes/legacy";
10
+ import { sha256 } from "@noble/hashes/sha2";
11
+
12
+ // Base64 decode
13
+ function base64ToUint8Array(base64: string): Uint8Array {
14
+ return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
15
+ }
16
+
17
+ // Base64 encode UTF-8 bytes
18
+ function toBase64Bytes(str: string): string {
19
+ const utf8 = new TextEncoder().encode(str);
20
+ let binary = "";
21
+ for (let i = 0; i < utf8.length; i++) {
22
+ binary += String.fromCharCode(utf8[i]);
23
+ }
24
+ return btoa(binary);
25
+ }
26
+
27
+ // Cosmos address derivation (ADR-36)
28
+ // https://github.com/cosmos/cosmos-sdk/blob/214b11dcbaa129f7b4c0013b2103db9d54b85e9e/docs/architecture/adr-036-arbitrary-signature.md
29
+ function deriveCosmosAddress(pubkey: Uint8Array, prefix = "cosmos"): string {
30
+ const sha = sha256(pubkey);
31
+ const rip = ripemd160(sha);
32
+ return bech32.encode(prefix, bech32.toWords(rip));
33
+ }
34
+
35
+ export async function verifyCosmosSignature(
36
+ proof: SignatureProof
37
+ ): Promise<SignatureProof> {
38
+ try {
39
+ const [ns, , address] = proof.address.split(/:/);
40
+ if (ns !== "cosmos") {
41
+ return { ...proof, status: ProofStatus.FAILED };
42
+ }
43
+
44
+ // Parse the proof JSON
45
+ const pubKeyBytes = base64ToUint8Array(
46
+ (proof.chainSpecificData as CosmosMetadata).pub_key.value
47
+ );
48
+ const signatureBytes = base64ToUint8Array(proof.proof);
49
+
50
+ // Step 1: Derive address from pubkey
51
+ const derivedAddress = deriveCosmosAddress(pubKeyBytes);
52
+ if (derivedAddress !== address) {
53
+ return { ...proof, status: ProofStatus.FAILED };
54
+ }
55
+
56
+ // Step 2: Build MsgSignData + StdSignDoc
57
+ const signDoc: StdSignDoc = {
58
+ chain_id: "", // must match the signing process
59
+ account_number: "0",
60
+ sequence: "0",
61
+ fee: { amount: [], gas: "0" },
62
+ msgs: [
63
+ {
64
+ type: "sign/MsgSignData",
65
+ value: {
66
+ signer: address,
67
+ data: toBase64Bytes(proof.attestation),
68
+ },
69
+ },
70
+ ],
71
+ memo: "",
72
+ };
73
+
74
+ // Step 3: Serialize + hash
75
+ const signBytes = await serializeSignDoc(signDoc);
76
+ const digest = sha256(signBytes);
77
+
78
+ // Step 4: Verify using secp256k1
79
+ const verified = secp256k1.verify(signatureBytes, digest, pubKeyBytes);
80
+
81
+ return {
82
+ ...proof,
83
+ status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,
84
+ };
85
+ } catch {
86
+ return { ...proof, status: ProofStatus.FAILED };
87
+ }
88
+ }
package/src/index.ts CHANGED
@@ -14,6 +14,7 @@ import { verifyCIP8Signature } from "./cardano";
14
14
  import { verifyPersonalSignXRPL } from "./xrpl";
15
15
  import { verifyStellarSignature } from "./xlm";
16
16
  import { verifyConcordiumSignature } from "./concordium";
17
+ import { verifyCosmosSignature } from "./cosmos";
17
18
 
18
19
  export async function verifyProof(
19
20
  proof: OwnershipProof,
@@ -46,9 +47,10 @@ export async function verifyProof(
46
47
  return verifyPersonalSignXRPL(proof as SignatureProof, publicKey);
47
48
  case ProofTypes.XLM_ED25519:
48
49
  return verifyStellarSignature(proof as SignatureProof);
49
- case ProofTypes.CONCORDIUM: {
50
+ case ProofTypes.CONCORDIUM:
50
51
  return verifyConcordiumSignature(proof as SignatureProof);
51
- }
52
+ case ProofTypes.COSMOS:
53
+ return verifyCosmosSignature(proof as SignatureProof);
52
54
  case ProofTypes.EIP712:
53
55
  case ProofTypes.BIP137:
54
56
  case ProofTypes.BIP322:
@@ -11,28 +11,37 @@ const bitcoinP2WPKHProof: SignatureProof = {
11
11
  type: ProofTypes.BIP137,
12
12
  wallet_provider: "Manual Wallet Signature",
13
13
  status: ProofStatus.PENDING,
14
- address: "bip122:000000000019d6689c085ae165831e93:3Q9jfjfbMDuatto7wZ6Tz2aKaDCRHqWWys",
14
+ address:
15
+ "bip122:000000000019d6689c085ae165831e93:3Q9jfjfbMDuatto7wZ6Tz2aKaDCRHqWWys",
15
16
  did: "did:pkh:bip122:000000000019d6689c085ae165831e93:3Q9jfjfbMDuatto7wZ6Tz2aKaDCRHqWWys",
16
- attestation: "I certify that the blockchain address 3Q9jfjfbMDuatto7wZ6Tz2aKaDCRHqWWys belongs to did:pkh:bip122:000000000019d6689c085ae165831e93:3Q9jfjfbMDuatto7wZ6Tz2aKaDCRHqWWys on Wed, 09 Jul 2025 20:17:12 GMT",
17
- proof: "HxPmbzvEnvgu0RYIPYl5bWySkFNXwOF/Jegq3NvzjFZ/Ik/koTdV9rh2A7osXefhzTlniUw8YbZNmCeXB9V9qC8=",
18
- }
17
+ attestation:
18
+ "I certify that the blockchain address 3Q9jfjfbMDuatto7wZ6Tz2aKaDCRHqWWys belongs to did:pkh:bip122:000000000019d6689c085ae165831e93:3Q9jfjfbMDuatto7wZ6Tz2aKaDCRHqWWys on Wed, 09 Jul 2025 20:17:12 GMT",
19
+ proof:
20
+ "HxPmbzvEnvgu0RYIPYl5bWySkFNXwOF/Jegq3NvzjFZ/Ik/koTdV9rh2A7osXefhzTlniUw8YbZNmCeXB9V9qC8=",
21
+ };
19
22
 
20
23
  const bitcoinP2shProof: SignatureProof = {
21
24
  type: ProofTypes.BIP137,
22
25
  wallet_provider: "Manual Wallet Signature",
23
26
  status: ProofStatus.PENDING,
24
- address: "bip122:000000000019d6689c085ae165831e93:1ANiqVALaKwadxA9nvmCUHpBhaopVniuVS",
27
+ address:
28
+ "bip122:000000000019d6689c085ae165831e93:1ANiqVALaKwadxA9nvmCUHpBhaopVniuVS",
25
29
  did: "did:pkh:bip122:000000000019d6689c085ae165831e93:1ANiqVALaKwadxA9nvmCUHpBhaopVniuVS",
26
- attestation: "I certify that the blockchain address 1ANiqVALaKwadxA9nvmCUHpBhaopVniuVS belongs to did:pkh:bip122:000000000019d6689c085ae165831e93:1ANiqVALaKwadxA9nvmCUHpBhaopVniuVS on Wed, 02 Jul 2025 16:01:44 GMT",
27
- proof: "IBo2Im6O5NyuXzBJ+coiTMqmUsqG9bv8NzM3+B5e+5XMB2Xp1n/sIsE/73Jy0EdSvcb334t49+3tdjE/X+sXjFw="
28
- }
30
+ attestation:
31
+ "I certify that the blockchain address 1ANiqVALaKwadxA9nvmCUHpBhaopVniuVS belongs to did:pkh:bip122:000000000019d6689c085ae165831e93:1ANiqVALaKwadxA9nvmCUHpBhaopVniuVS on Wed, 02 Jul 2025 16:01:44 GMT",
32
+ proof:
33
+ "IBo2Im6O5NyuXzBJ+coiTMqmUsqG9bv8NzM3+B5e+5XMB2Xp1n/sIsE/73Jy0EdSvcb334t49+3tdjE/X+sXjFw=",
34
+ };
29
35
 
30
36
  const bip322SegwitTestnetProof: SignatureProof = {
31
37
  type: ProofTypes.BIP137,
32
- address: "bip122:000000000019d6689c085ae165831e93:tb1q0apvgsh48f3rmw224na3ye5rrg37fd796cm0xf",
38
+ address:
39
+ "bip122:000000000019d6689c085ae165831e93:tb1q0apvgsh48f3rmw224na3ye5rrg37fd796cm0xf",
33
40
  did: "did:pkh:bip122:000000000019d6689c085ae165831e93:tb1q0apvgsh48f3rmw224na3ye5rrg37fd796cm0xf",
34
- attestation: "I certify that the blockchain address tb1q0apvgsh48f3rmw224na3ye5rrg37fd796cm0xf belongs to did:pkh:bip122:000000000019d6689c085ae165831e93:tb1q0apvgsh48f3rmw224na3ye5rrg37fd796cm0xf on Tue, 29 Apr 2025 15:33:26 GMT",
35
- proof: "AkcwRAIgXzKIOGjVp8qrqpUM+e/2BkbITsbGpjTFxgfHjk/Bt+sCIGueO3l6/cvfZYSggu2F4WKj1K97p32NivFwKdOHg9cgASEDtJ0tfDgrGYO4OSuYjyiehIoY1dEUkPo8XZQJ3Yqo//8=",
41
+ attestation:
42
+ "I certify that the blockchain address tb1q0apvgsh48f3rmw224na3ye5rrg37fd796cm0xf belongs to did:pkh:bip122:000000000019d6689c085ae165831e93:tb1q0apvgsh48f3rmw224na3ye5rrg37fd796cm0xf on Tue, 29 Apr 2025 15:33:26 GMT",
43
+ proof:
44
+ "AkcwRAIgXzKIOGjVp8qrqpUM+e/2BkbITsbGpjTFxgfHjk/Bt+sCIGueO3l6/cvfZYSggu2F4WKj1K97p32NivFwKdOHg9cgASEDtJ0tfDgrGYO4OSuYjyiehIoY1dEUkPo8XZQJ3Yqo//8=",
36
45
  status: ProofStatus.PENDING,
37
46
  wallet_provider: "Manual Wallet Signature",
38
47
  };
@@ -100,10 +109,13 @@ const dogeProof: SignatureProof = {
100
109
 
101
110
  const zcashProof: SignatureProof = {
102
111
  type: ProofTypes.BIP137,
103
- address: "bip122:000000000019d6689c085ae165831e93:t1NmCz1oRS3e84NrUHsbHPJnz8a6KgZvbHL",
112
+ address:
113
+ "bip122:000000000019d6689c085ae165831e93:t1NmCz1oRS3e84NrUHsbHPJnz8a6KgZvbHL",
104
114
  did: "did:pkh:bip122:000000000019d6689c085ae165831e93:t1NmCz1oRS3e84NrUHsbHPJnz8a6KgZvbHL",
105
- attestation: "I certify that the blockchain address t1NmCz1oRS3e84NrUHsbHPJnz8a6KgZvbHL belongs to did:pkh:bip122:000000000019d6689c085ae165831e93:t1NmCz1oRS3e84NrUHsbHPJnz8a6KgZvbHL on Mon, 30 Jun 2025 08:41:29 GMT",
106
- proof: "HzPW+q7EXixhtV/rBQ0sOtoXCDOml7C6P6rtIxLkBy3tenP978po7tuwj997DvJRiakL65Qw7xADK5hHs1Vu7es=",
115
+ attestation:
116
+ "I certify that the blockchain address t1NmCz1oRS3e84NrUHsbHPJnz8a6KgZvbHL belongs to did:pkh:bip122:000000000019d6689c085ae165831e93:t1NmCz1oRS3e84NrUHsbHPJnz8a6KgZvbHL on Mon, 30 Jun 2025 08:41:29 GMT",
117
+ proof:
118
+ "HzPW+q7EXixhtV/rBQ0sOtoXCDOml7C6P6rtIxLkBy3tenP978po7tuwj997DvJRiakL65Qw7xADK5hHs1Vu7es=",
107
119
  status: ProofStatus.PENDING,
108
120
  wallet_provider: "Manual Wallet Signature",
109
121
  };
@@ -111,12 +123,18 @@ const zcashProof: SignatureProof = {
111
123
  describe("verifyBTCSignature", () => {
112
124
  it("handles bitcoin p2sh addresses", async () => {
113
125
  const result = await verifyBTCSignature(bitcoinP2shProof);
114
- expect(result).toEqual({ ...bitcoinP2shProof, status: ProofStatus.VERIFIED });
126
+ expect(result).toEqual({
127
+ ...bitcoinP2shProof,
128
+ status: ProofStatus.VERIFIED,
129
+ });
115
130
  });
116
131
 
117
132
  it("handles bitcoin p2wpkh addresses", async () => {
118
133
  const result = await verifyBTCSignature(bitcoinP2WPKHProof);
119
- expect(result).toEqual({ ...bitcoinP2WPKHProof, status: ProofStatus.VERIFIED });
134
+ expect(result).toEqual({
135
+ ...bitcoinP2WPKHProof,
136
+ status: ProofStatus.VERIFIED,
137
+ });
120
138
  });
121
139
 
122
140
  it("handles bip322 segwit testnet addresses", async () => {
@@ -416,4 +434,75 @@ describe("verifyBTCSignature", () => {
416
434
  status: ProofStatus.VERIFIED,
417
435
  });
418
436
  });
437
+
438
+ it("handles sparrow wallet p2tr taproot addresses", async () => {
439
+ const taprootProof: SignatureProof = {
440
+ type: ProofTypes.BIP322,
441
+ address:
442
+ "bip122:000000000019d6689c085ae165831e93:bc1ppxwwc2hvpcfftkt08qf5rqdduw7j9dxskpugk438a7uvmqx7u5nqfdtacv",
443
+ did: "did:pkh:bip122:000000000019d6689c085ae165831e93:bc1ppxwwc2hvpcfftkt08qf5rqdduw7j9dxskpugk438a7uvmqx7u5nqfdtacv",
444
+ attestation: "example",
445
+ proof:
446
+ "AUFBIMYTvgwZX5oQLE8TmIdOITYB20CarEVU9JJJo1RTIh7RWyoYpMG2REkkqEz7zBLVK9nBu5JXM17/Boxdh8cWAQ==",
447
+ status: ProofStatus.PENDING,
448
+ wallet_provider: "TaprootWallet",
449
+ };
450
+
451
+ const result = await verifyBTCSignature(taprootProof);
452
+ expect(result.status).toBe(ProofStatus.VERIFIED);
453
+ });
454
+
455
+ it("handles an unknown wallet p2tr taproot addresses", async () => {
456
+ const taprootProof: SignatureProof = {
457
+ type: ProofTypes.BIP322,
458
+ address:
459
+ "bip122:000000000019d6689c085ae165831e93:bc1p6rvvgjwj594jr4sr3e8gd0nhvkr63ujwdm6g2mv9fqx4lv60suwsa9ff6w",
460
+ did: "did:pkh:bip122:000000000019d6689c085ae165831e93:bc1p6rvvgjwj594jr4sr3e8gd0nhvkr63ujwdm6g2mv9fqx4lv60suwsa9ff6w",
461
+ attestation:
462
+ "I certify that the blockchain address bc1p6rvvgjwj594jr4sr3e8gd0nhvkr63ujwdm6g2mv9fqx4lv60suwsa9ff6w belongs to did:pkh:bip122:000000000019d6689c085ae165831e93:bc1p6rvvgjwj594jr4sr3e8gd0nhvkr63ujwdm6g2mv9fqx4lv60suwsa9ff6w on Thu, 17 Jul 2025 16:08:33 GMT",
463
+ proof:
464
+ "AUFqBSD6l9VStmdpN3mcPRb6sb/LxuwonL4JNHQ3BH1DlOdhxbUVbyNFHpGoqFuhlZjjCWkjt6SpdGzIGgcW2mClAQ==",
465
+ status: ProofStatus.PENDING,
466
+ wallet_provider: "TaprootWallet",
467
+ };
468
+
469
+ const result = await verifyBTCSignature(taprootProof);
470
+ expect(result.status).toBe(ProofStatus.VERIFIED);
471
+ });
472
+
473
+ it("fails for invalid taproot signature", async () => {
474
+ const taprootProof: SignatureProof = {
475
+ type: ProofTypes.BIP322,
476
+ address:
477
+ "bip122:000000000019d6689c085ae165831e93:bc1p6rvvgjwj594jr4sr3e8gd0nhvkr63ujwdm6g2mv9fqx4lv60suwsa9ff6w",
478
+ did: "did:pkh:bip122:000000000019d6689c085ae165831e93:bc1p6rvvgjwj594jr4sr3e8gd0nhvkr63ujwdm6g2mv9fqx4lv60suwsa9ff6w",
479
+ attestation:
480
+ "I certify that the blockchain address bc1p6rvvgjwj594jr4sr3e8gd0nhvkr63ujwdm6g2mv9fqx4lv60suwsa9ff6w belongs to did:pkh:bip122:000000000019d6689c085ae165831e93:bc1p6rvvgjwj594jr4sr3e8gd0nhvkr63ujwdm6g2mv9fqx4lv60suwsa9ff6w on Thu, 17 Jul 2025 16:08:33 GMT",
481
+ proof:
482
+ "AUFBIMYTvgwZX5oQLE8TmIdOITYB20CarEVU9JJJo1RTIh7RWyoYpMG2REkkqEz7zBLVK9nBu5JXM17/Boxdh8cWAQ==",
483
+ status: ProofStatus.PENDING,
484
+ wallet_provider: "Sparrow Wallet",
485
+ };
486
+
487
+ const result = await verifyBTCSignature(taprootProof);
488
+ expect(result.status).toBe(ProofStatus.FAILED);
489
+ });
490
+
491
+ it("it verifies sparrow wallet btc wallet proof", async () => {
492
+ const sparrowWalletProof: SignatureProof = {
493
+ type: ProofTypes.BIP137,
494
+ status: ProofStatus.PENDING,
495
+ did: "did:pkh:bip122:000000000019d6689c085ae165831e93:bc1p6rvvgjwj594jr4sr3e8gd0nhvkr63ujwdm6g2mv9fqx4lv60suwsa9ff6w",
496
+ address:
497
+ "bip122:000000000019d6689c085ae165831e93:bc1p6rvvgjwj594jr4sr3e8gd0nhvkr63ujwdm6g2mv9fqx4lv60suwsa9ff6w",
498
+ proof:
499
+ "AUHV/fUKDdHSdP90WwkLBRq0miuMOnfBtlLvG7HqC013vWfj+wWuDRD0bvg5LLHd/IQUqMtgyZZs96IX8E3EtauLAQ==",
500
+ attestation:
501
+ "I certify that the blockchain address bc1p6rvvgjwj594jr4sr3e8gd0nhvkr63ujwdm6g2mv9fqx4lv60suwsa9ff6w belongs to did:pkh:bip122:000000000019d6689c085ae165831e93:bc1p6rvvgjwj594jr4sr3e8gd0nhvkr63ujwdm6g2mv9fqx4lv60suwsa9ff6w on Thu, 11 Sep 2025 13:19:19 GMT",
502
+ wallet_provider: "Manual Wallet Signature",
503
+ };
504
+
505
+ const result = await verifyBTCSignature(sparrowWalletProof);
506
+ expect(result.status).toBe(ProofStatus.VERIFIED);
507
+ });
419
508
  });
@@ -0,0 +1,51 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ ProofStatus,
4
+ ProofTypes,
5
+ SignatureProof,
6
+ } from "@notabene/javascript-sdk";
7
+ import { verifyCosmosSignature } from "../cosmos";
8
+
9
+ describe("verifyCosmosSignature", () => {
10
+ it("returns verified proof when valid message and address", async () => {
11
+ const proof: SignatureProof = {
12
+ type: ProofTypes.COSMOS,
13
+ did: "did:pkh:cosmos:cosmoshub-4:cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8",
14
+ status: ProofStatus.PENDING,
15
+ attestation:
16
+ "I certify that\n\ncosmos:cosmoshub-4 account cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8\n\nbelonged to did:key:z6MksamQq4oVRktkeSDxs6pbixYFUaUca8xgCvnTVLQA5PC5\n\non Mon, 14 Jul 2025 15:09:56 GMT",
17
+ address:
18
+ "cosmos:cosmoshub-4:cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8",
19
+ proof:
20
+ "r0L+4864I3p2jPRBt8IAnhhvYmemMkKGg9fcKM0GOBIScgIpmyX/+ObPT/09VMSCzxlzZkH6LQGLNiD2sudAZw==",
21
+ wallet_provider: "Keplr (Wallet Connect)",
22
+ chainSpecificData: {
23
+ pub_key: {
24
+ type: "tendermint/PubKeySecp256k1",
25
+ value: "Ak8k3jYgH4srYi1ygG0GWLrdbF7VwQgjiLqjifuJSsmU",
26
+ },
27
+ },
28
+ };
29
+ const result = await verifyCosmosSignature(proof);
30
+
31
+ expect(result.status).toBe(ProofStatus.VERIFIED);
32
+ });
33
+
34
+ it("returns failed proof when invalid message and address", async () => {
35
+ const proof: SignatureProof = {
36
+ type: ProofTypes.COSMOS,
37
+ did: "did:pkh:cosmos:cosmoshub-4:cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8",
38
+ status: ProofStatus.PENDING,
39
+ attestation:
40
+ "I certify that\n\ncosmos:cosmoshub-4 account cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8\n\nbelonged to did:key:z6MksamQq4oVRktkeSDxs6pbixYFUaUca8xgCvnTVLQA5PC5\n\non Mon, 14 Jul 2025 15:09:56 GMT",
41
+ address:
42
+ "cosmos:cosmoshub-4:cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8",
43
+ proof:
44
+ '{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Ak8k3jYgH4srYi1ygG0GWLrdbF7VwQgjiLqjifuJSsmU"},"signature":"invalid_signature_that_will_fail_verification"}',
45
+ wallet_provider: "Keplr (Wallet Connect)",
46
+ };
47
+ const result = await verifyCosmosSignature(proof);
48
+
49
+ expect(result.status).toBe(ProofStatus.FAILED);
50
+ });
51
+ });
@@ -342,4 +342,27 @@ describe("verifyProof", () => {
342
342
  expect(result.status).toBe(ProofStatus.VERIFIED);
343
343
  });
344
344
  })
345
+
346
+ describe("Cosmos", () => {
347
+ const proof: SignatureProof = {
348
+ type: ProofTypes.COSMOS,
349
+ did: "did:pkh:cosmos:cosmoshub-4:cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8",
350
+ status: ProofStatus.PENDING,
351
+ attestation: "I certify that\n\ncosmos:cosmoshub-4 account cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8\n\nbelonged to did:key:z6MksamQq4oVRktkeSDxs6pbixYFUaUca8xgCvnTVLQA5PC5\n\non Mon, 14 Jul 2025 15:09:56 GMT",
352
+ address: "cosmos:cosmoshub-4:cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8",
353
+ proof: "r0L+4864I3p2jPRBt8IAnhhvYmemMkKGg9fcKM0GOBIScgIpmyX/+ObPT/09VMSCzxlzZkH6LQGLNiD2sudAZw==",
354
+ wallet_provider: "Keplr (Wallet Connect)",
355
+ chainSpecificData: {
356
+ pub_key: {
357
+ type: "tendermint/PubKeySecp256k1",
358
+ value: "Ak8k3jYgH4srYi1ygG0GWLrdbF7VwQgjiLqjifuJSsmU",
359
+ },
360
+ }
361
+ };
362
+
363
+ it("should verify proof", async () => {
364
+ const result = await verifyProof(proof);
365
+ expect(result.status).toBe(ProofStatus.VERIFIED);
366
+ });
367
+ });
345
368
  });
package/src/xlm.ts CHANGED
@@ -18,8 +18,7 @@ export function verifyStellarSignature(
18
18
  ...proof,
19
19
  status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,
20
20
  };
21
- } catch (error) {
22
- console.error(error);
21
+ } catch {
23
22
  return { ...proof, status: ProofStatus.FAILED };
24
23
  }
25
24
  }