signet.js 0.0.2 → 0.0.3

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.
Files changed (71) hide show
  1. package/.eslintrc.json +55 -0
  2. package/.prettierrc +1 -0
  3. package/babel.config.js +6 -0
  4. package/docs/pages/index.mdx +36 -0
  5. package/docs/pages/signetjs/advanced/chain-signatures-contract.mdx +52 -0
  6. package/docs/pages/signetjs/advanced/chain.mdx +45 -0
  7. package/docs/pages/signetjs/chains/bitcoin/bitcoin.mdx +171 -0
  8. package/docs/pages/signetjs/chains/bitcoin/btc-rpc-adapter.mdx +26 -0
  9. package/docs/pages/signetjs/chains/cosmos.mdx +171 -0
  10. package/docs/pages/signetjs/chains/evm.mdx +319 -0
  11. package/docs/pages/signetjs/contract-addresses.mdx +27 -0
  12. package/docs/pages/signetjs/index.mdx +88 -0
  13. package/docs/snippets/code/contract.ts +21 -0
  14. package/docs/snippets/code/evm/env.ts +16 -0
  15. package/docs/snippets/code/near/env.ts +13 -0
  16. package/hardhat.config.mts +19 -0
  17. package/package.json +1 -1
  18. package/src/chains/Bitcoin/BTCRpcAdapter/BTCRpcAdapter.ts +11 -0
  19. package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/Mempool.ts +96 -0
  20. package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/index.ts +1 -0
  21. package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/types.ts +72 -0
  22. package/src/chains/Bitcoin/BTCRpcAdapter/index.ts +6 -0
  23. package/src/chains/Bitcoin/Bitcoin.ts +287 -0
  24. package/src/chains/Bitcoin/types.ts +48 -0
  25. package/src/chains/Bitcoin/utils.ts +14 -0
  26. package/src/chains/Chain.ts +92 -0
  27. package/src/chains/ChainSignatureContract.ts +65 -0
  28. package/src/chains/Cosmos/Cosmos.ts +258 -0
  29. package/src/chains/Cosmos/types.ts +35 -0
  30. package/src/chains/Cosmos/utils.ts +45 -0
  31. package/src/chains/EVM/EVM.test.ts +238 -0
  32. package/src/chains/EVM/EVM.ts +334 -0
  33. package/src/chains/EVM/types.ts +53 -0
  34. package/src/chains/EVM/utils.ts +27 -0
  35. package/src/chains/index.ts +38 -0
  36. package/src/chains/types.ts +46 -0
  37. package/src/index.ts +2 -0
  38. package/src/utils/chains/evm/ChainSignaturesContract.ts +286 -0
  39. package/src/utils/chains/evm/ChainSignaturesContractABI.ts +359 -0
  40. package/src/utils/chains/evm/errors.ts +52 -0
  41. package/src/utils/chains/evm/index.ts +3 -0
  42. package/src/utils/chains/evm/types.ts +28 -0
  43. package/src/utils/chains/evm/utils.ts +11 -0
  44. package/src/utils/chains/index.ts +2 -0
  45. package/src/utils/chains/near/ChainSignatureContract.ts +155 -0
  46. package/src/utils/chains/near/account.ts +42 -0
  47. package/src/utils/chains/near/constants.ts +4 -0
  48. package/src/utils/chains/near/index.ts +3 -0
  49. package/src/utils/chains/near/signAndSend/index.ts +1 -0
  50. package/src/utils/chains/near/signAndSend/keypair.ts +178 -0
  51. package/src/utils/chains/near/transactionBuilder.ts +73 -0
  52. package/src/utils/chains/near/types.ts +77 -0
  53. package/src/utils/constants.ts +62 -0
  54. package/src/utils/cryptography.ts +131 -0
  55. package/src/utils/index.ts +3 -0
  56. package/src/utils/publicKey.ts +23 -0
  57. package/tsconfig.eslint.json +8 -0
  58. package/tsconfig.json +122 -0
  59. package/tsup.config.ts +55 -0
  60. package/vitest.config.ts +16 -0
  61. package/vocs.config.ts +73 -0
  62. package/browser/index.browser.cjs +0 -3
  63. package/browser/index.browser.cjs.map +0 -1
  64. package/browser/index.browser.js +0 -3
  65. package/browser/index.browser.js.map +0 -1
  66. package/node/index.node.cjs +0 -3
  67. package/node/index.node.cjs.map +0 -1
  68. package/node/index.node.js +0 -3
  69. package/node/index.node.js.map +0 -1
  70. package/types/index.d.cts +0 -919
  71. package/types/index.d.ts +0 -919
@@ -0,0 +1,334 @@
1
+ import {
2
+ createPublicClient,
3
+ http,
4
+ parseTransaction,
5
+ type PublicClient,
6
+ hashMessage,
7
+ hashTypedData,
8
+ keccak256,
9
+ toBytes,
10
+ type Hex,
11
+ serializeTransaction,
12
+ type Signature,
13
+ numberToHex,
14
+ getAddress,
15
+ type Address,
16
+ type Hash,
17
+ concatHex,
18
+ encodeAbiParameters,
19
+ hexToBigInt,
20
+ concat,
21
+ pad,
22
+ isAddress,
23
+ } from 'viem'
24
+
25
+ import { Chain } from '@chains/Chain'
26
+ import type { BaseChainSignatureContract } from '@chains/ChainSignatureContract'
27
+ import type {
28
+ EVMTransactionRequest,
29
+ EVMUnsignedTransaction,
30
+ EVMMessage,
31
+ EVMTypedData,
32
+ UserOperationV6,
33
+ UserOperationV7,
34
+ } from '@chains/EVM/types'
35
+ import { fetchEVMFeeProperties } from '@chains/EVM/utils'
36
+ import type { HashToSign, RSVSignature, KeyDerivationPath } from '@chains/types'
37
+
38
+ /**
39
+ * Implementation of the Chain interface for EVM-compatible networks.
40
+ * Handles interactions with Ethereum Virtual Machine based blockchains like Ethereum, BSC, Polygon, etc.
41
+ */
42
+ export class EVM extends Chain<EVMTransactionRequest, EVMUnsignedTransaction> {
43
+ private readonly client: PublicClient
44
+ private readonly contract: BaseChainSignatureContract
45
+
46
+ /**
47
+ * Creates a new EVM chain instance
48
+ * @param params - Configuration parameters
49
+ * @param params.rpcUrl - URL of the EVM JSON-RPC provider (e.g., Infura endpoint)
50
+ * @param params.contract - Instance of the chain signature contract for MPC operations
51
+ */
52
+ constructor({
53
+ rpcUrl,
54
+ contract,
55
+ }: {
56
+ rpcUrl: string
57
+ contract: BaseChainSignatureContract
58
+ }) {
59
+ super()
60
+
61
+ this.contract = contract
62
+ this.client = createPublicClient({
63
+ transport: http(rpcUrl),
64
+ })
65
+ }
66
+
67
+ private async attachGasAndNonce(
68
+ transaction: EVMTransactionRequest
69
+ ): Promise<EVMUnsignedTransaction> {
70
+ const fees = await fetchEVMFeeProperties(this.client, transaction)
71
+ const nonce = await this.client.getTransactionCount({
72
+ address: transaction.from,
73
+ })
74
+
75
+ const { from, ...rest } = transaction
76
+
77
+ return {
78
+ ...fees,
79
+ nonce,
80
+ chainId: Number(await this.client.getChainId()),
81
+ type: 'eip1559',
82
+ ...rest,
83
+ }
84
+ }
85
+
86
+ private transformRSVSignature(signature: RSVSignature): Signature {
87
+ return {
88
+ r: `0x${signature.r}`,
89
+ s: `0x${signature.s}`,
90
+ yParity: signature.v - 27,
91
+ }
92
+ }
93
+
94
+ private assembleSignature(signature: RSVSignature): Hex {
95
+ const { r, s, yParity } = this.transformRSVSignature(signature)
96
+
97
+ if (yParity === undefined) {
98
+ throw new Error('Missing yParity')
99
+ }
100
+
101
+ return concatHex([r, s, numberToHex(yParity + 27, { size: 1 })])
102
+ }
103
+
104
+ async deriveAddressAndPublicKey(
105
+ predecessor: string,
106
+ path: KeyDerivationPath
107
+ ): Promise<{
108
+ address: string
109
+ publicKey: string
110
+ }> {
111
+ const uncompressedPubKey = await this.contract.getDerivedPublicKey({
112
+ path,
113
+ predecessor,
114
+ })
115
+
116
+ if (!uncompressedPubKey) {
117
+ throw new Error('Failed to get derived public key')
118
+ }
119
+
120
+ const publicKeyNoPrefix = uncompressedPubKey.startsWith('04')
121
+ ? uncompressedPubKey.slice(2)
122
+ : uncompressedPubKey
123
+
124
+ const hash = keccak256(Buffer.from(publicKeyNoPrefix, 'hex'))
125
+ const address = getAddress(`0x${hash.slice(-40)}`)
126
+
127
+ return {
128
+ address,
129
+ publicKey: uncompressedPubKey,
130
+ }
131
+ }
132
+
133
+ async getBalance(
134
+ address: string
135
+ ): Promise<{ balance: bigint; decimals: number }> {
136
+ const balance = await this.client.getBalance({
137
+ address: address as Address,
138
+ })
139
+ return {
140
+ balance,
141
+ decimals: 18,
142
+ }
143
+ }
144
+
145
+ serializeTransaction(transaction: EVMUnsignedTransaction): `0x${string}` {
146
+ return serializeTransaction(transaction)
147
+ }
148
+
149
+ deserializeTransaction(serialized: `0x${string}`): EVMUnsignedTransaction {
150
+ return parseTransaction(serialized) as EVMUnsignedTransaction
151
+ }
152
+
153
+ async prepareTransactionForSigning(
154
+ transactionRequest: EVMTransactionRequest
155
+ ): Promise<{
156
+ transaction: EVMUnsignedTransaction
157
+ hashesToSign: HashToSign[]
158
+ }> {
159
+ const transaction = await this.attachGasAndNonce(transactionRequest)
160
+
161
+ const serializedTx = serializeTransaction(transaction)
162
+ const txHash = toBytes(keccak256(serializedTx))
163
+
164
+ return {
165
+ transaction,
166
+ hashesToSign: [Array.from(txHash)],
167
+ }
168
+ }
169
+
170
+ async prepareMessageForSigning(message: EVMMessage): Promise<{
171
+ hashesToSign: HashToSign[]
172
+ }> {
173
+ return {
174
+ hashesToSign: [Array.from(toBytes(hashMessage(message)))],
175
+ }
176
+ }
177
+
178
+ async prepareTypedDataForSigning(typedDataRequest: EVMTypedData): Promise<{
179
+ hashesToSign: HashToSign[]
180
+ }> {
181
+ return {
182
+ hashesToSign: [Array.from(toBytes(hashTypedData(typedDataRequest)))],
183
+ }
184
+ }
185
+
186
+ /**
187
+ * This implementation is a common step for Biconomy and Alchemy.
188
+ * Key differences between implementations:
189
+ * - Signature format: Biconomy omits 0x00 prefix when concatenating, Alchemy includes it
190
+ * - Version support: Biconomy only supports v6, Alchemy supports both v6 and v7
191
+ * - Validation: Biconomy uses modules for signature validation, Alchemy uses built-in validation
192
+ */
193
+ async prepareUserOpForSigning(
194
+ userOp: UserOperationV7 | UserOperationV6,
195
+ entryPointAddress?: Address,
196
+ chainIdArgs?: number
197
+ ): Promise<{
198
+ userOp: UserOperationV7 | UserOperationV6
199
+ hashesToSign: HashToSign[]
200
+ }> {
201
+ const chainId = chainIdArgs ?? (await this.client.getChainId())
202
+ const entryPoint =
203
+ entryPointAddress || '0x0000000071727De22E5E9d8BAf0edAc6f37da032'
204
+
205
+ const encoded = encodeAbiParameters(
206
+ [{ type: 'bytes32' }, { type: 'address' }, { type: 'uint256' }],
207
+ [
208
+ keccak256(
209
+ encodeAbiParameters(
210
+ [
211
+ { type: 'address' },
212
+ { type: 'uint256' },
213
+ { type: 'bytes32' },
214
+ { type: 'bytes32' },
215
+ { type: 'bytes32' },
216
+ { type: 'uint256' },
217
+ { type: 'bytes32' },
218
+ { type: 'bytes32' },
219
+ ],
220
+ [
221
+ userOp.sender,
222
+ hexToBigInt(userOp.nonce),
223
+ keccak256(
224
+ 'factory' in userOp &&
225
+ 'factoryData' in userOp &&
226
+ userOp.factory &&
227
+ userOp.factoryData
228
+ ? concat([userOp.factory, userOp.factoryData])
229
+ : 'initCode' in userOp
230
+ ? userOp.initCode
231
+ : '0x'
232
+ ),
233
+ keccak256(userOp.callData),
234
+ concat([
235
+ pad(userOp.verificationGasLimit, { size: 16 }),
236
+ pad(userOp.callGasLimit, { size: 16 }),
237
+ ]),
238
+ hexToBigInt(userOp.preVerificationGas),
239
+ concat([
240
+ pad(userOp.maxPriorityFeePerGas, { size: 16 }),
241
+ pad(userOp.maxFeePerGas, { size: 16 }),
242
+ ]),
243
+ keccak256(
244
+ 'paymaster' in userOp &&
245
+ userOp.paymaster &&
246
+ isAddress(userOp.paymaster)
247
+ ? concat([
248
+ userOp.paymaster,
249
+ pad(userOp.paymasterVerificationGasLimit, { size: 16 }),
250
+ pad(userOp.paymasterPostOpGasLimit, { size: 16 }),
251
+ userOp.paymasterData,
252
+ ])
253
+ : 'paymasterAndData' in userOp
254
+ ? userOp.paymasterAndData
255
+ : '0x'
256
+ ),
257
+ ]
258
+ )
259
+ ),
260
+ entryPoint,
261
+ BigInt(chainId),
262
+ ]
263
+ )
264
+
265
+ const userOpHash = keccak256(encoded)
266
+
267
+ return {
268
+ userOp,
269
+ hashesToSign: [Array.from(toBytes(hashMessage({ raw: userOpHash })))],
270
+ }
271
+ }
272
+
273
+ attachTransactionSignature({
274
+ transaction,
275
+ rsvSignatures,
276
+ }: {
277
+ transaction: EVMUnsignedTransaction
278
+ rsvSignatures: RSVSignature[]
279
+ }): `0x02${string}` {
280
+ const signature = this.transformRSVSignature(rsvSignatures[0])
281
+
282
+ return serializeTransaction(transaction, signature)
283
+ }
284
+
285
+ assembleMessageSignature({
286
+ rsvSignatures,
287
+ }: {
288
+ rsvSignatures: RSVSignature[]
289
+ }): Hex {
290
+ return this.assembleSignature(rsvSignatures[0])
291
+ }
292
+
293
+ assembleTypedDataSignature({
294
+ rsvSignatures,
295
+ }: {
296
+ rsvSignatures: RSVSignature[]
297
+ }): Hex {
298
+ return this.assembleSignature(rsvSignatures[0])
299
+ }
300
+
301
+ attachUserOpSignature({
302
+ userOp,
303
+ rsvSignatures,
304
+ }: {
305
+ userOp: UserOperationV7 | UserOperationV6
306
+ rsvSignatures: RSVSignature[]
307
+ }): UserOperationV7 | UserOperationV6 {
308
+ const { r, s, yParity } = this.transformRSVSignature(rsvSignatures[0])
309
+ if (yParity === undefined) {
310
+ throw new Error('Missing yParity')
311
+ }
312
+
313
+ return {
314
+ ...userOp,
315
+ signature: concatHex([
316
+ '0x00', // Alchemy specific implementation. Biconomy doesn't include the 0x00 prefix.
317
+ r,
318
+ s,
319
+ numberToHex(Number(yParity + 27), { size: 1 }),
320
+ ]),
321
+ }
322
+ }
323
+
324
+ async broadcastTx(txSerialized: `0x${string}`): Promise<Hash> {
325
+ try {
326
+ return await this.client.sendRawTransaction({
327
+ serializedTransaction: txSerialized,
328
+ })
329
+ } catch (error) {
330
+ console.error('Transaction broadcast failed:', error)
331
+ throw new Error('Failed to broadcast transaction.')
332
+ }
333
+ }
334
+ }
@@ -0,0 +1,53 @@
1
+ import type {
2
+ Address,
3
+ Hex,
4
+ TransactionRequest,
5
+ TypedDataDefinition,
6
+ SignableMessage,
7
+ } from 'viem'
8
+
9
+ export type EVMUnsignedTransaction = TransactionRequest & {
10
+ type: 'eip1559'
11
+ chainId: number
12
+ }
13
+
14
+ export interface EVMTransactionRequest
15
+ extends Omit<EVMUnsignedTransaction, 'chainId' | 'type'> {
16
+ from: Address
17
+ }
18
+
19
+ export type EVMMessage = SignableMessage
20
+
21
+ export type EVMTypedData = TypedDataDefinition
22
+
23
+ export interface UserOperationV7 {
24
+ sender: Hex
25
+ nonce: Hex
26
+ factory: Hex
27
+ factoryData: Hex
28
+ callData: Hex
29
+ callGasLimit: Hex
30
+ verificationGasLimit: Hex
31
+ preVerificationGas: Hex
32
+ maxFeePerGas: Hex
33
+ maxPriorityFeePerGas: Hex
34
+ paymaster: Hex
35
+ paymasterVerificationGasLimit: Hex
36
+ paymasterPostOpGasLimit: Hex
37
+ paymasterData: Hex
38
+ signature: Hex
39
+ }
40
+
41
+ export interface UserOperationV6 {
42
+ sender: Hex
43
+ nonce: Hex
44
+ initCode: Hex
45
+ callData: Hex
46
+ callGasLimit: Hex
47
+ verificationGasLimit: Hex
48
+ preVerificationGas: Hex
49
+ maxFeePerGas: Hex
50
+ maxPriorityFeePerGas: Hex
51
+ paymasterAndData: Hex
52
+ signature: Hex
53
+ }
@@ -0,0 +1,27 @@
1
+ import { type PublicClient, type TransactionRequest } from 'viem'
2
+
3
+ export interface EVMFeeProperties {
4
+ gas: bigint
5
+ maxFeePerGas: bigint
6
+ maxPriorityFeePerGas: bigint
7
+ }
8
+
9
+ export async function fetchEVMFeeProperties(
10
+ client: PublicClient,
11
+ transaction: TransactionRequest
12
+ ): Promise<EVMFeeProperties> {
13
+ const [gas, feeData] = await Promise.all([
14
+ client.estimateGas(transaction),
15
+ client.estimateFeesPerGas(),
16
+ ])
17
+
18
+ const maxFeePerGas = feeData.maxFeePerGas ?? BigInt(10_000_000_000) // 10 gwei
19
+ const maxPriorityFeePerGas =
20
+ feeData.maxPriorityFeePerGas ?? BigInt(10_000_000_000) // 10 gwei
21
+
22
+ return {
23
+ gas,
24
+ maxFeePerGas,
25
+ maxPriorityFeePerGas,
26
+ }
27
+ }
@@ -0,0 +1,38 @@
1
+ export { Chain } from './Chain'
2
+ export { ChainSignatureContract, type SignArgs } from './ChainSignatureContract'
3
+ export * from './types'
4
+
5
+ // EVM
6
+ export { EVM } from './EVM/EVM'
7
+
8
+ export { fetchEVMFeeProperties } from './EVM/utils'
9
+
10
+ export type {
11
+ EVMTransactionRequest,
12
+ EVMUnsignedTransaction,
13
+ EVMMessage,
14
+ EVMTypedData,
15
+ } from './EVM/types'
16
+
17
+ // Bitcoin
18
+ export { Bitcoin } from './Bitcoin/Bitcoin'
19
+
20
+ export { BTCRpcAdapters, BTCRpcAdapter } from './Bitcoin/BTCRpcAdapter'
21
+
22
+ export type {
23
+ BTCTransactionRequest,
24
+ BTCUnsignedTransaction,
25
+ BTCTransaction,
26
+ BTCOutput,
27
+ BTCInput,
28
+ BTCNetworkIds,
29
+ } from './Bitcoin/types'
30
+
31
+ // Cosmos
32
+ export { Cosmos } from './Cosmos/Cosmos'
33
+
34
+ export type {
35
+ CosmosNetworkIds,
36
+ CosmosTransactionRequest,
37
+ CosmosUnsignedTransaction,
38
+ } from './Cosmos/types'
@@ -0,0 +1,46 @@
1
+ import { type SignArgs } from '@chains/ChainSignatureContract'
2
+
3
+ export type HashToSign = SignArgs['payload']
4
+
5
+ type Base58String = string
6
+
7
+ export type NajPublicKey = `secp256k1:${Base58String}`
8
+
9
+ export type UncompressedPubKeySEC1 = `04${string}`
10
+
11
+ export type CompressedPubKeySEC1 = `02${string}` | `03${string}`
12
+
13
+ export type KeyDerivationPath = string
14
+
15
+ export interface RSVSignature {
16
+ r: string
17
+ s: string
18
+ v: number
19
+ }
20
+
21
+ export interface NearNearMpcSignature {
22
+ big_r: {
23
+ affine_point: string
24
+ }
25
+ s: {
26
+ scalar: string
27
+ }
28
+ recovery_id: number
29
+ }
30
+
31
+ export interface SigNetNearMpcSignature {
32
+ big_r: string
33
+ s: string
34
+ recovery_id: number
35
+ }
36
+
37
+ export interface SigNetEvmMpcSignature {
38
+ bigR: { x: bigint; y: bigint }
39
+ s: bigint
40
+ recoveryId: number
41
+ }
42
+
43
+ export type MPCSignature =
44
+ | NearNearMpcSignature
45
+ | SigNetNearMpcSignature
46
+ | SigNetEvmMpcSignature
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * as utils from './utils'
2
+ export * from './chains'