Skip to content

Commit 91327ed

Browse files
committed
feat: bitcoin vm normalize and validate
1 parent 8020cd6 commit 91327ed

File tree

3 files changed

+61
-0
lines changed

3 files changed

+61
-0
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"@sinclair/typebox": "^0.34.14",
1515
"@solana/web3.js": "^1.98.2",
1616
"axios": "^1.8.3",
17+
"bech32": "^2.0.0",
1718
"bs58": "^6.0.0",
1819
"fastify": "^5.2.1",
1920
"node-cron": "^3.0.3",

src/models/utils.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { VmType } from "@reservoir0x/relay-protocol-sdk";
22
import { PublicKey } from "@solana/web3.js";
3+
import { bech32, bech32m } from "bech32";
4+
import bs58 from "bs58";
35

46
import { externalError } from "../common/error";
57

@@ -57,6 +59,46 @@ export const nvAddress = (address: string, vmType: VmType) => {
5759
}
5860
}
5961

62+
case "bitcoin-vm": {
63+
try {
64+
// For bech32 addresses (P2WPKH)
65+
try {
66+
const decoded = bech32.decode(address);
67+
// Validate that it's a Bitcoin address by checking the prefix
68+
if (decoded.prefix !== "bc" && decoded.prefix !== "tb") {
69+
throw new Error("Invalid Bitcoin address prefix");
70+
}
71+
return address;
72+
} catch (e1) {
73+
// For bech32m addresses (P2TR)
74+
try {
75+
const decoded = bech32m.decode(address);
76+
// Validate that it's a Bitcoin address by checking the prefix
77+
if (decoded.prefix !== "bc" && decoded.prefix !== "tb") {
78+
throw new Error("Invalid Bitcoin address prefix");
79+
}
80+
return address;
81+
} catch (e2) {
82+
// For P2PKH / P2SH (base58 encoded)
83+
bs58.decode(address);
84+
85+
// Validate address format
86+
// P2PKH starts with 1, P2SH starts with 3
87+
if (address.startsWith("1") || address.startsWith("3")) {
88+
// Additional validation: check length
89+
if (address.length < 26 || address.length > 35) {
90+
throw new Error("Invalid Bitcoin address length");
91+
}
92+
return address;
93+
}
94+
throw new Error("Invalid Bitcoin address format");
95+
}
96+
}
97+
} catch (e) {
98+
throw externalError(`Invalid address: ${address}`);
99+
}
100+
}
101+
60102
default: {
61103
throw externalError("Vm type not implemented");
62104
}
@@ -66,6 +108,8 @@ export const nvAddress = (address: string, vmType: VmType) => {
66108
// Normalize and validate a currency
67109
export const nvCurrency = (currency: string, vmType: VmType) => {
68110
switch (vmType) {
111+
112+
case "bitcoin-vm":
69113
case "ethereum-vm": {
70114
const requiredLengthInBytes = 20;
71115

@@ -131,6 +175,17 @@ export const nvTransactionId = (transactionId: string, vmType: VmType) => {
131175
return transactionId;
132176
}
133177

178+
case "bitcoin-vm": {
179+
const requiredLengthInBytes = 32;
180+
181+
const hexString = nvBytes(transactionId, requiredLengthInBytes);
182+
if (hexString.length !== 2 + requiredLengthInBytes * 2) {
183+
throw externalError(`Invalid transaction id: ${transactionId}`);
184+
}
185+
186+
return hexString;
187+
}
188+
134189
default: {
135190
throw externalError("Vm type not implemented");
136191
}

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,11 @@ base64-js@^1.3.1:
12751275
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
12761276
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
12771277

1278+
bech32@^2.0.0:
1279+
version "2.0.0"
1280+
resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355"
1281+
integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==
1282+
12781283
bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1:
12791284
version "5.2.2"
12801285
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.2.tgz#82c09f9ebbb17107cd72cb7fd39bd1f9d0aaa566"

0 commit comments

Comments
 (0)