@notabene/verify-proof 1.0.0-preview.7 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
package/dist/solana.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { SignatureProof } from "@notabene/javascript-sdk/src/types";
1
+ import { SignatureProof } from "@notabene/javascript-sdk";
2
2
  export declare function verifySolanaSignature(proof: SignatureProof): Promise<SignatureProof>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notabene/verify-proof",
3
- "version": "1.0.0-preview.7",
3
+ "version": "1.0.0",
4
4
  "description": "Verify ownership proofs",
5
5
  "source": "src/index.ts",
6
6
  "type": "module",
@@ -23,19 +23,12 @@
23
23
  "test": "vitest"
24
24
  },
25
25
  "devDependencies": {
26
- "@types/bitcoinjs-lib": "3.4.0",
27
- "@types/node": "^22.9.0",
28
- "bitcoinjs-lib": "^3.2.0",
29
- "buffer": "^6.0.3",
30
- "ecpair": "^3.0.0-rc.0",
31
26
  "eslint": "^9.9.0",
32
27
  "microbundle": "^0.15.1",
33
- "tiny-secp256k1": "^2.2.3",
28
+ "ox": "^0.2.2",
34
29
  "typescript": "^5.5.4",
35
30
  "vite": "^5.4.11",
36
- "vitest": "^2.0.5",
37
- "bitcoinjs-message": "^2.2.0",
38
- "ox": "^0.1.4"
31
+ "vitest": "^2.0.5"
39
32
  },
40
33
  "dependencies": {
41
34
  "@bitauth/libauth": "^3.0.0",
@@ -45,7 +38,6 @@
45
38
  "bigi": "^1.4.2",
46
39
  "bs58": "^6.0.0",
47
40
  "tweetnacl": "^1.0.3",
48
- "varuint-bitcoin": "^2.0.0",
49
- "viem": "^2.21.44"
41
+ "varuint-bitcoin": "^2.0.0"
50
42
  }
51
43
  }
package/src/bitcoin.ts CHANGED
@@ -1,7 +1,4 @@
1
- import {
2
- ProofStatus,
3
- SignatureProof,
4
- } from "@notabene/javascript-sdk/src/types";
1
+ import { ProofStatus, SignatureProof } from "@notabene/javascript-sdk";
5
2
  import { bech32 } from "bech32";
6
3
 
7
4
  import {
@@ -21,7 +18,7 @@ enum SEGWIT_TYPES {
21
18
 
22
19
  const messagePrefix = "\u0018Bitcoin Signed Message:\n";
23
20
 
24
- export enum DerivationMode {
21
+ enum DerivationMode {
25
22
  LEGACY = "Legacy",
26
23
  NATIVE = "Native SegWit",
27
24
  SEGWIT = "SegWit",
@@ -77,6 +74,7 @@ type DecodedSignature = {
77
74
  recovery: RecoveryId;
78
75
  signature: Uint8Array;
79
76
  };
77
+
80
78
  function decodeSignature(proof: string): DecodedSignature {
81
79
  const signature = decodeBase64(proof);
82
80
  if (signature.length !== 65) throw new Error("Invalid signature length");
@@ -146,7 +144,7 @@ function verify(
146
144
  return actual === address;
147
145
  }
148
146
 
149
- export function magicHash(attestation: string) {
147
+ function magicHash(attestation: string) {
150
148
  const prefix = new TextEncoder().encode(messagePrefix);
151
149
  const message = new TextEncoder().encode(attestation);
152
150
  const length = encodeLength(message.length).buffer;
package/src/eth.ts CHANGED
@@ -1,8 +1,22 @@
1
- import {
2
- ProofStatus,
3
- SignatureProof,
4
- } from "@notabene/javascript-sdk/src/types";
5
- import { verifyMessage } from "viem";
1
+ import { ProofStatus, SignatureProof } from "@notabene/javascript-sdk";
2
+
3
+ import { Secp256k1, Hex, PersonalMessage, Signature, Address } from "ox";
4
+
5
+ export function verifyEIP191(
6
+ address: Hex.Hex,
7
+ message: string,
8
+ proof: Hex.Hex,
9
+ ): boolean {
10
+ try {
11
+ const payload = PersonalMessage.getSignPayload(Hex.fromString(message));
12
+ const signature = Signature.fromHex(proof);
13
+ const publicKey = Secp256k1.recoverPublicKey({ payload, signature });
14
+ const recovered = Address.fromPublicKey(publicKey);
15
+ return recovered.toString() === address.toString();
16
+ } catch (error) {
17
+ return false;
18
+ }
19
+ }
6
20
 
7
21
  export async function verifyPersonalSignEIP191(
8
22
  proof: SignatureProof,
@@ -10,11 +24,11 @@ export async function verifyPersonalSignEIP191(
10
24
  const [ns, _, address] = proof.address.split(/:/);
11
25
  if (ns !== "eip155") return { ...proof, status: ProofStatus.FAILED };
12
26
 
13
- const verified = await verifyMessage({
14
- address: address as `0x${string}`,
15
- message: proof.attestation,
16
- signature: proof.proof as `0x${string}`,
17
- });
27
+ const verified = verifyEIP191(
28
+ address as Hex.Hex,
29
+ proof.attestation,
30
+ proof.proof as Hex.Hex,
31
+ );
18
32
  return {
19
33
  ...proof,
20
34
  status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,
package/src/index.ts CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  ScreenshotProof,
6
6
  ProofTypes,
7
7
  ProofStatus,
8
- } from "@notabene/javascript-sdk/src/types";
8
+ } from "@notabene/javascript-sdk";
9
9
  import { verifyBTCSignature } from "./bitcoin";
10
10
  import { verifyPersonalSignEIP191 } from "./eth";
11
11
  import { verifySolanaSignature } from "./solana";
package/src/solana.ts CHANGED
@@ -1,10 +1,8 @@
1
1
  import nacl from "tweetnacl";
2
- import {
3
- ProofStatus,
4
- SignatureProof,
5
- } from "@notabene/javascript-sdk/src/types";
2
+ import { ProofStatus, SignatureProof } from "@notabene/javascript-sdk";
6
3
  import { decode as decodeBase64 } from "@stablelib/base64";
7
4
  import bs58 from "bs58";
5
+
8
6
  export async function verifySolanaSignature(
9
7
  proof: SignatureProof,
10
8
  ): Promise<SignatureProof> {
@@ -1,99 +1,61 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import * as bitcoin from "bitcoinjs-lib";
3
- import * as bitcoinMessage from "bitcoinjs-message";
4
- import { Buffer } from "node:buffer";
5
2
 
6
3
  import { verifyBTCSignature } from "../bitcoin";
7
4
  import {
8
5
  type SignatureProof,
9
6
  ProofStatus,
10
7
  ProofTypes,
11
- } from "@notabene/javascript-sdk/src/types";
12
-
13
- function rng() {
14
- return Buffer.from("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz");
15
- }
16
-
17
- function signMessage(
18
- toAddress: (kp: bitcoin.ECPair) => string,
19
- ): SignatureProof {
20
- const keyPair = bitcoin.ECPair.makeRandom({ rng: rng });
21
- const address = toAddress(keyPair);
22
- const sigOptions: bitcoinMessage.SignatureOptions | undefined =
23
- address.startsWith("bc1") ? { segwitType: "p2wpkh" } : undefined;
24
- const privateKey = keyPair.d.toBuffer(32);
25
- var attestation = "This is an example of a signed message.";
26
-
27
- var proof = bitcoinMessage
28
- .sign(attestation, privateKey, keyPair.compressed, sigOptions)
29
- .toString("base64");
30
- // console.log("signMessage", { proof, address });
31
- return {
32
- type: ProofTypes.BIP137,
33
- address: `bip122:000000000019d6689c085ae165831e93:${address}`,
34
- did: `did:pkh:bip122:000000000019d6689c085ae165831e93:${address}`,
35
- attestation,
36
- proof,
37
- status: ProofStatus.PENDING,
38
- wallet_provider: "MetaMask",
39
- };
40
- }
41
-
42
- function signP2PKHMessage(): SignatureProof {
43
- return signMessage((kp) => kp.getAddress());
44
- }
45
-
46
- function signSegWitMessage(): SignatureProof {
47
- return signMessage((kp) =>
48
- bitcoin.address.toBech32(
49
- bitcoin.crypto.hash160(kp.getPublicKeyBuffer()),
50
- 0,
51
- kp.network.bech32 || "bc",
52
- ),
53
- );
54
- }
8
+ } from "@notabene/javascript-sdk";
9
+
10
+ const segwitProof: SignatureProof = {
11
+ type: ProofTypes.BIP137,
12
+ address:
13
+ "bip122:000000000019d6689c085ae165831e93:bc1qnf4kpa62dwhpwm0stsas5yv0skatt3v9s040p8",
14
+ did: "did:pkh:bip122:000000000019d6689c085ae165831e93:bc1qnf4kpa62dwhpwm0stsas5yv0skatt3v9s040p8",
15
+ attestation: "This is an example of a signed message.",
16
+ proof:
17
+ "J796FDv8f8w3syiaMSGoL6SAwPLRf6t13S+fYNjYA9EnJy3T0jZOY1eHBaGTBufOuW78FVFSwXKyUnrEjYOT9EU=",
18
+ status: ProofStatus.PENDING,
19
+ wallet_provider: "BitMask",
20
+ };
21
+
22
+ const legacyProof: SignatureProof = {
23
+ type: ProofTypes.BIP137,
24
+ address:
25
+ "bip122:000000000019d6689c085ae165831e93:1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64",
26
+ did: "did:pkh:bip122:000000000019d6689c085ae165831e93:1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64",
27
+ attestation: "This is an example of a signed message.",
28
+ proof:
29
+ "H796FDv8f8w3syiaMSGoL6SAwPLRf6t13S+fYNjYA9EnJy3T0jZOY1eHBaGTBufOuW78FVFSwXKyUnrEjYOT9EU=",
30
+ status: ProofStatus.PENDING,
31
+ wallet_provider: "BitMask",
32
+ };
55
33
 
56
34
  describe("verifyBTCSignature", () => {
57
35
  it("handles native segwit addresses", async () => {
58
- const proof: SignatureProof = signSegWitMessage();
59
- const result = await verifyBTCSignature(proof);
60
- expect(result.status).toBe(ProofStatus.VERIFIED);
36
+ const result = await verifyBTCSignature(segwitProof);
37
+ expect(result).toEqual({ ...segwitProof, status: ProofStatus.VERIFIED });
61
38
  });
62
39
 
63
40
  it("verifies legacy bitcoin address signature", async () => {
64
- const proof: SignatureProof = signP2PKHMessage();
65
- const result = await verifyBTCSignature(proof);
66
- expect(result.status).toBe(ProofStatus.VERIFIED);
41
+ const result = await verifyBTCSignature(legacyProof);
42
+ expect(result).toEqual({ ...legacyProof, status: ProofStatus.VERIFIED });
67
43
  });
68
44
 
69
45
  it("fails for invalid bitcoin signature", async () => {
70
- const proof: SignatureProof = {
71
- type: ProofTypes.BIP137,
72
- did: "did:pkh:bip122:000000000019d6689c085ae165831e93:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
73
- wallet_provider: "ledger",
74
- address:
75
- "bip122:000000000019d6689c085ae165831e93:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
76
- attestation: "test message",
77
- proof: "invalid_signature",
78
- status: ProofStatus.PENDING,
79
- };
46
+ const proof: SignatureProof = { ...legacyProof, proof: "invalitd" };
80
47
 
81
48
  const result = await verifyBTCSignature(proof);
82
- expect(result.status).toBe(ProofStatus.FAILED);
49
+ expect(result).toEqual({ ...proof, status: ProofStatus.FAILED });
83
50
  });
84
51
 
85
52
  it("returns failed proof for non-BIP122 address", async () => {
86
53
  const proof: SignatureProof = {
87
- type: ProofTypes.EIP191,
88
- status: ProofStatus.PENDING,
89
- did: "did:example:123",
54
+ ...legacyProof,
90
55
  address: "eip155:1:0x1234567890123456789012345678901234567890",
91
- proof: "0x1234567890",
92
- attestation: "I own this address",
93
- wallet_provider: "MetaMask",
94
56
  };
95
57
 
96
58
  const result = await verifyBTCSignature(proof);
97
- expect(result.status).toBe(ProofStatus.FAILED);
59
+ expect(result).toEqual({ ...proof, status: ProofStatus.FAILED });
98
60
  });
99
61
  });
@@ -4,7 +4,7 @@ import {
4
4
  type SignatureProof,
5
5
  ProofStatus,
6
6
  ProofTypes,
7
- } from "@notabene/javascript-sdk/src/types";
7
+ } from "@notabene/javascript-sdk";
8
8
 
9
9
  import { Hex, Address, PersonalMessage, Secp256k1, Signature } from "ox";
10
10
 
@@ -1,4 +1,4 @@
1
- import { describe, it, expect, vi } from "vitest";
1
+ import { describe, it, expect } from "vitest";
2
2
  import { verifyProof } from "../index";
3
3
  import {
4
4
  type DeclarationProof,
@@ -6,13 +6,7 @@ import {
6
6
  type SignatureProof,
7
7
  ProofStatus,
8
8
  ProofTypes,
9
- } from "@notabene/javascript-sdk/src/types";
10
- import { verifyMessage } from "viem";
11
-
12
- // Mock viem's verifyMessage function
13
- vi.mock("viem", () => ({
14
- verifyMessage: vi.fn(),
15
- }));
9
+ } from "@notabene/javascript-sdk";
16
10
 
17
11
  describe("verifyProof", () => {
18
12
  it("should verify a confirmed checkbox proof", async () => {
@@ -87,68 +81,86 @@ describe("verifyProof", () => {
87
81
  expect(result).toEqual(proof);
88
82
  });
89
83
 
90
- it("should verify a valid PersonalSignEIP191 proof", async () => {
84
+ describe("eip191", () => {
91
85
  const proof: SignatureProof = {
92
86
  type: ProofTypes.EIP191,
87
+ address: "eip155:1:0x772f226b9cc93a2953dc5868864de72f90441caf",
88
+ did: "did:pkh:eip155:1:0x772f226b9cc93a2953dc5868864de72f90441caf",
89
+ attestation: "I agree",
90
+ proof:
91
+ "0xddd74ca17084728a167b6f99326b50a0ce4a1ce4f80a56da4a60b23b69637c624e6dfb2270de56caece9182200fe38791339cbf097586a4e06a9ef4bd298cc1800",
93
92
  status: ProofStatus.PENDING,
94
- did: "did:example:123",
95
- address: "eip155:1:0x1234567890123456789012345678901234567890",
96
- proof: "0x1234567890",
97
- attestation: "I own this address",
98
93
  wallet_provider: "MetaMask",
99
94
  };
100
95
 
101
- // Mock successful verification
102
- (verifyMessage as jest.Mock).mockResolvedValue(true);
96
+ it("should verify proof", async () => {
97
+ const result = await verifyProof(proof);
98
+ expect(result.status).toBe(ProofStatus.VERIFIED);
99
+ });
103
100
 
104
- const result = await verifyProof(proof);
101
+ it("should fail if not invalid proof", async () => {
102
+ const result = await verifyProof({
103
+ ...proof,
104
+ proof: "0x",
105
+ } as SignatureProof);
105
106
 
106
- expect(result.status).toBe(ProofStatus.VERIFIED);
107
- expect(verifyMessage).toHaveBeenCalledWith({
108
- address: "0x1234567890123456789012345678901234567890",
109
- message: proof.attestation,
110
- signature: proof.proof,
107
+ expect(result.status).toBe(ProofStatus.FAILED);
111
108
  });
112
109
  });
113
110
 
114
- it("should fail an invalid PersonalSignEIP191 proof", async () => {
111
+ describe("solana", () => {
115
112
  const proof: SignatureProof = {
116
- type: ProofTypes.EIP191,
113
+ type: ProofTypes.ED25519,
117
114
  status: ProofStatus.PENDING,
118
- did: "did:example:123",
119
- address: "eip155:1:0x1234567890123456789012345678901234567890",
120
- proof: "0x1234567890",
121
- attestation: "I own this address",
122
- wallet_provider: "MetaMask",
115
+ did: "did:pkh:solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:3GgW39JvdBVCAdiK1mafeBcBKXJq8mee5woVoDARRnPq",
116
+ address:
117
+ "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:3GgW39JvdBVCAdiK1mafeBcBKXJq8mee5woVoDARRnPq",
118
+ attestation: "Test message",
119
+ proof:
120
+ "8dkeX3S+qisPkadV5QkmLWu2gbom/ljqlEwDAVuEV33HWZbSbPol+KV/zET4NLn8JBvSpu6JROJRg45WAIMPAQ==",
121
+ wallet_provider: "Phantom",
123
122
  };
124
123
 
125
- // Mock failed verification
126
- (verifyMessage as jest.Mock).mockResolvedValue(false);
124
+ it("should verify proof", async () => {
125
+ const result = await verifyProof(proof);
126
+ expect(result.status).toBe(ProofStatus.VERIFIED);
127
+ });
127
128
 
128
- const result = await verifyProof(proof);
129
+ it("should fail if not invalid proof", async () => {
130
+ const result = await verifyProof({
131
+ ...proof,
132
+ proof: "0x",
133
+ } as SignatureProof);
129
134
 
130
- expect(result.status).toBe(ProofStatus.FAILED);
131
- expect(verifyMessage).toHaveBeenCalledWith({
132
- address: "0x1234567890123456789012345678901234567890",
133
- message: proof.attestation,
134
- signature: proof.proof,
135
+ expect(result.status).toBe(ProofStatus.FAILED);
135
136
  });
136
137
  });
137
138
 
138
- it("should fail if not eip155 proof", async () => {
139
+ describe("bitcoin", () => {
139
140
  const proof: SignatureProof = {
140
- type: ProofTypes.EIP191,
141
+ type: ProofTypes.BIP137,
141
142
  status: ProofStatus.PENDING,
142
- did: "did:example:123",
143
143
  address:
144
- "bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6",
145
- proof: "0x1234567890",
146
- attestation: "I own this address",
147
- wallet_provider: "MetaMask",
144
+ "bip122:000000000019d6689c085ae165831e93:bc1qnf4kpa62dwhpwm0stsas5yv0skatt3v9s040p8",
145
+ did: "did:pkh:bip122:000000000019d6689c085ae165831e93:bc1qnf4kpa62dwhpwm0stsas5yv0skatt3v9s040p8",
146
+ attestation: "This is an example of a signed message.",
147
+ proof:
148
+ "J796FDv8f8w3syiaMSGoL6SAwPLRf6t13S+fYNjYA9EnJy3T0jZOY1eHBaGTBufOuW78FVFSwXKyUnrEjYOT9EU=",
149
+ wallet_provider: "Phantom",
148
150
  };
149
151
 
150
- const result = await verifyProof(proof);
152
+ it("should verify proof", async () => {
153
+ const result = await verifyProof(proof);
154
+ expect(result.status).toBe(ProofStatus.VERIFIED);
155
+ });
151
156
 
152
- expect(result.status).toBe(ProofStatus.FAILED);
157
+ it("should fail if not invalid proof", async () => {
158
+ const result = await verifyProof({
159
+ ...proof,
160
+ proof: "0x",
161
+ } as SignatureProof);
162
+
163
+ expect(result.status).toBe(ProofStatus.FAILED);
164
+ });
153
165
  });
154
166
  });
@@ -5,7 +5,7 @@ import {
5
5
  ProofStatus,
6
6
  ProofTypes,
7
7
  SignatureProof,
8
- } from "@notabene/javascript-sdk/src/types";
8
+ } from "@notabene/javascript-sdk";
9
9
  import { verifySolanaSignature } from "../solana";
10
10
 
11
11
  describe("verifySolanaSignature", () => {
@@ -25,6 +25,7 @@ describe("verifySolanaSignature", () => {
25
25
  proof: Buffer.from(signature).toString("base64"),
26
26
  wallet_provider: "Phantom",
27
27
  };
28
+
28
29
  const result = await verifySolanaSignature(proof);
29
30
  expect(result.status).toBe(ProofStatus.VERIFIED);
30
31
  });