@w3lib/solana-batch-call 0.1.0 → 0.2.1

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.
package/src/address.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { ed25519 } from '@noble/curves/ed25519.js'
2
1
  import { sha256 } from '@noble/hashes/sha2.js'
3
2
  import { base58 } from '@scure/base'
4
3
 
@@ -28,14 +27,57 @@ export function encodeAddress(bytes: Uint8Array): string {
28
27
  return base58.encode(bytes)
29
28
  }
30
29
 
31
- /** 32 字节是否在 ed25519 曲线上(PDA 必须不在曲线上) */
32
- function isOnCurve(bytes: Uint8Array): boolean {
33
- try {
34
- ed25519.Point.fromBytes(bytes)
35
- return true
36
- } catch {
30
+ // ed25519:-x² + y² = 1 + d·x²·y²(mod p)
31
+ const P = 2n ** 255n - 19n
32
+ const D = 37095705934669439343138083508754565189542113879843219016388785533085940283555n
33
+
34
+ /** x^(2^k) mod p */
35
+ function pow2k(x: bigint, k: number): bigint {
36
+ let r = x
37
+ for (let i = 0; i < k; i++) r = (r * r) % P
38
+ return r
39
+ }
40
+
41
+ /** x^((p-1)/2) mod p(欧拉判别法),(p-1)/2 = (2^250 - 1)·2^4 + 6,用加法链:约 254 次平方 + 12 次乘法 */
42
+ function legendre(x: bigint): bigint {
43
+ const b2 = (((x * x) % P) * x) % P // x^(2^2-1)
44
+ const b4 = (pow2k(b2, 2) * b2) % P
45
+ const b5 = (pow2k(b4, 1) * x) % P
46
+ const b10 = (pow2k(b5, 5) * b5) % P
47
+ const b20 = (pow2k(b10, 10) * b10) % P
48
+ const b40 = (pow2k(b20, 20) * b20) % P
49
+ const b80 = (pow2k(b40, 40) * b40) % P
50
+ const b160 = (pow2k(b80, 80) * b80) % P
51
+ const b240 = (pow2k(b160, 80) * b80) % P
52
+ const b250 = (pow2k(b240, 10) * b10) % P
53
+ return (pow2k(b250, 4) * ((b2 * b2) % P)) % P
54
+ }
55
+
56
+ /**
57
+ * 32 字节是否是合法的 ed25519 压缩点(PDA 必须不在曲线上)。
58
+ * 按 RFC 8032 5.1.3 解压缩,与 @noble/curves 的 Point.fromBytes(web3.js 的 isOnCurve 同样如此)一致:
59
+ * y ≥ p、x = 0 但符号位为 1 都算不在曲线上。只需判断 x² = (y² - 1) / (d·y² + 1) 有没有解,不必真的开方
60
+ */
61
+ export function isOnCurve(bytes: Uint8Array): boolean {
62
+ if (bytes.length !== 32) {
63
+ return false
64
+ }
65
+ let y = 0n
66
+ for (let i = 31; i >= 0; i--) {
67
+ y = (y << 8n) | BigInt(i === 31 ? (bytes[i] as number) & 0x7f : (bytes[i] as number))
68
+ }
69
+ if (y >= P) {
37
70
  return false
38
71
  }
72
+ const y2 = (y * y) % P
73
+ const u = (y2 - 1n + P) % P
74
+ const v = (D * y2 + 1n) % P // d 不是平方数,v 不会为 0
75
+ if (u === 0n) {
76
+ // x = 0:符号位必须为 0
77
+ return ((bytes[31] as number) & 0x80) === 0
78
+ }
79
+ // u / v 是平方数 ⇔ u·v 是平方数(欧拉判别法)
80
+ return legendre((u * v) % P) === 1n
39
81
  }
40
82
 
41
83
  const PDA_MARKER = new TextEncoder().encode('ProgramDerivedAddress')
@@ -50,14 +92,24 @@ function concat(parts: Uint8Array[]): Uint8Array {
50
92
  return out
51
93
  }
52
94
 
95
+ const MAX_SEED_LENGTH = 32
96
+ const MAX_SEEDS = 16
97
+
53
98
  // PDA 推导是纯计算但要做多次 sha256 + 曲线判断,结果缓存
54
99
  const pdaCache = new Map<string, string>()
55
100
 
56
101
  /**
57
102
  * 推导 PDA(与 web3.js PublicKey.findProgramAddressSync 相同),返回地址。
58
- * seeds 每项不超过 32 字节。
103
+ * seeds 每项不超过 32 字节、最多 15 个(加上 bump 共 16 个,链上上限),超出时抛错。
59
104
  */
60
105
  export function findProgramAddress(seeds: Uint8Array[], programId: string): string {
106
+ // 与链上和 web3.js 一致:每个种子最多 32 字节;种子加上 bump 最多 16 个
107
+ if (seeds.some((seed) => seed.length > MAX_SEED_LENGTH)) {
108
+ throw new TypeError('Max seed length exceeded')
109
+ }
110
+ if (seeds.length + 1 > MAX_SEEDS) {
111
+ throw new TypeError('Max seeds exceeded')
112
+ }
61
113
  const key = `${programId}:${seeds.map((s) => encodeAddress(s)).join(',')}`
62
114
  const cached = pdaCache.get(key)
63
115
  if (cached) {