signet.js 0.0.6 → 0.0.7

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 (182) hide show
  1. package/.eslintrc.json +67 -0
  2. package/.prettierrc +1 -0
  3. package/babel.config.js +6 -0
  4. package/docs/dist/.vocs/icons/arrow-diagonal.svg +3 -0
  5. package/docs/dist/.vocs/icons/chevron-down.svg +13 -0
  6. package/docs/dist/.vocs/icons/chevron-up.svg +13 -0
  7. package/docs/dist/.vocs/icons/link.svg +3 -0
  8. package/docs/dist/.vocs/search-index-7b499e25.json +1 -0
  9. package/docs/dist/assets/arbitrary-hash-Cd6eo8ZD.js +309 -0
  10. package/docs/dist/assets/broadcast-tx-CeTEE9yX.js +8 -0
  11. package/docs/dist/assets/btc-rpc-adapter-C-qSHpFV.js +226 -0
  12. package/docs/dist/assets/chain-adapter-interface-B9TpOgQv.js +1280 -0
  13. package/docs/dist/assets/chain-contract-interface-DEku3k45.js +392 -0
  14. package/docs/dist/assets/constructor-73n7bp3b.js +161 -0
  15. package/docs/dist/assets/constructor-Bg7nvLe0.js +14 -0
  16. package/docs/dist/assets/contract-addresses-BYlrAOs3.js +200 -0
  17. package/docs/dist/assets/derive-address-and-public-key-DExrKiGV.js +14 -0
  18. package/docs/dist/assets/finalize-message-signing-W435d71R.js +20 -0
  19. package/docs/dist/assets/finalize-transaction-signing-BIgJ2dnc.js +36 -0
  20. package/docs/dist/assets/finalize-transaction-signing-C--HJs8D.js +24 -0
  21. package/docs/dist/assets/finalize-transaction-signing-CjGmN7d9.js +24 -0
  22. package/docs/dist/assets/finalize-typed-data-signing-CEOp_GWt.js +21 -0
  23. package/docs/dist/assets/get-balance-DBC-i6KG.js +13 -0
  24. package/docs/dist/assets/get-current-signature-deposit-BXm9AzYy.js +6 -0
  25. package/docs/dist/assets/get-current-signature-deposit-nOw4j1MN.js +6 -0
  26. package/docs/dist/assets/get-derived-public-key-BXvfo2m2.js +14 -0
  27. package/docs/dist/assets/get-derived-public-key-DQ1pyiFS.js +14 -0
  28. package/docs/dist/assets/get-latest-key-version-DWlkMCre.js +6 -0
  29. package/docs/dist/assets/get-public-key-B4PFoVqu.js +6 -0
  30. package/docs/dist/assets/get-public-key-B9xkYkD_.js +6 -0
  31. package/docs/dist/assets/index-BFuwoY4w.js +601 -0
  32. package/docs/dist/assets/index-C62Mf-vy.js +426 -0
  33. package/docs/dist/assets/index-D8xhaiVb.js +121 -0
  34. package/docs/dist/assets/index-DTr0DlO0.js +36 -0
  35. package/docs/dist/assets/index-V9dXf-ik.js +457 -0
  36. package/docs/dist/assets/prepare-message-for-signing-DESTq-Hg.js +16 -0
  37. package/docs/dist/assets/prepare-transaction-for-signing-DIKTU0zj.js +33 -0
  38. package/docs/dist/assets/prepare-transaction-for-signing-DV_wkZ5g.js +21 -0
  39. package/docs/dist/assets/prepare-transaction-for-signing-LVDP0COu.js +33 -0
  40. package/docs/dist/assets/prepare-typed-data-for-signing-CWcmJvw0.js +192 -0
  41. package/docs/dist/assets/sign-CwtS5LnB.js +13 -0
  42. package/docs/dist/assets/sign-OQxf9yn7.js +15 -0
  43. package/docs/dist/assets/signet-quick-start-CQK52nVG.js +350 -0
  44. package/docs/dist/assets/sponsor-foreign-chain-gas-C9iWXM8Q.js +1 -0
  45. package/docs/dist/assets/style-CKGXuRqx.css +1 -0
  46. package/docs/dist/examples/arbitrary-hash/index.html +88 -0
  47. package/docs/dist/examples/sponsor-foreign-chain-gas/index.html +21 -0
  48. package/docs/dist/index.html +56 -0
  49. package/docs/dist/initializeTheme.iife.js +1 -0
  50. package/docs/dist/introduction/signet-quick-start/index.html +109 -0
  51. package/docs/dist/primitives/chain-adapter-interface/index.html +515 -0
  52. package/docs/dist/primitives/chain-contract-interface/index.html +306 -0
  53. package/docs/dist/primitives/contract-addresses/index.html +97 -0
  54. package/docs/dist/signet-logo.png +0 -0
  55. package/docs/dist/signetjs/chain-adapters/bitcoin/btc-rpc-adapter/index.html +148 -0
  56. package/docs/dist/signetjs/chain-adapters/bitcoin/finalize-transaction-signing/index.html +41 -0
  57. package/docs/dist/signetjs/chain-adapters/bitcoin/index.html +187 -0
  58. package/docs/dist/signetjs/chain-adapters/bitcoin/prepare-transaction-for-signing/index.html +34 -0
  59. package/docs/dist/signetjs/chain-adapters/broadcast-tx/index.html +28 -0
  60. package/docs/dist/signetjs/chain-adapters/cosmos/finalize-transaction-signing/index.html +41 -0
  61. package/docs/dist/signetjs/chain-adapters/cosmos/index.html +166 -0
  62. package/docs/dist/signetjs/chain-adapters/cosmos/prepare-transaction-for-signing/index.html +43 -0
  63. package/docs/dist/signetjs/chain-adapters/derive-address-and-public-key/index.html +31 -0
  64. package/docs/dist/signetjs/chain-adapters/evm/finalize-message-signing/index.html +38 -0
  65. package/docs/dist/signetjs/chain-adapters/evm/finalize-transaction-signing/index.html +41 -0
  66. package/docs/dist/signetjs/chain-adapters/evm/finalize-typed-data-signing/index.html +39 -0
  67. package/docs/dist/signetjs/chain-adapters/evm/index.html +129 -0
  68. package/docs/dist/signetjs/chain-adapters/evm/prepare-message-for-signing/index.html +31 -0
  69. package/docs/dist/signetjs/chain-adapters/evm/prepare-transaction-for-signing/index.html +34 -0
  70. package/docs/dist/signetjs/chain-adapters/evm/prepare-typed-data-for-signing/index.html +49 -0
  71. package/docs/dist/signetjs/chain-adapters/get-balance/index.html +30 -0
  72. package/docs/dist/signetjs/contracts/evm/constructor/index.html +45 -0
  73. package/docs/dist/signetjs/contracts/evm/get-current-signature-deposit/index.html +26 -0
  74. package/docs/dist/signetjs/contracts/evm/get-derived-public-key/index.html +31 -0
  75. package/docs/dist/signetjs/contracts/evm/get-latest-key-version/index.html +26 -0
  76. package/docs/dist/signetjs/contracts/evm/get-public-key/index.html +26 -0
  77. package/docs/dist/signetjs/contracts/evm/sign/index.html +32 -0
  78. package/docs/dist/signetjs/contracts/near/constructor/index.html +34 -0
  79. package/docs/dist/signetjs/contracts/near/get-current-signature-deposit/index.html +26 -0
  80. package/docs/dist/signetjs/contracts/near/get-derived-public-key/index.html +31 -0
  81. package/docs/dist/signetjs/contracts/near/get-public-key/index.html +26 -0
  82. package/docs/dist/signetjs/contracts/near/sign/index.html +32 -0
  83. package/docs/pages/examples/arbitrary-hash.mdx +73 -0
  84. package/docs/pages/examples/sponsor-foreign-chain-gas.mdx +1 -0
  85. package/docs/pages/index.mdx +36 -0
  86. package/docs/pages/introduction/signet-quick-start.mdx +88 -0
  87. package/docs/pages/primitives/chain-adapter-interface.mdx +45 -0
  88. package/docs/pages/primitives/chain-contract-interface.mdx +52 -0
  89. package/docs/pages/primitives/contract-addresses.mdx +27 -0
  90. package/docs/pages/signetjs/chain-adapters/bitcoin/btc-rpc-adapter.mdx +26 -0
  91. package/docs/pages/signetjs/chain-adapters/bitcoin/finalize-transaction-signing.mdx +47 -0
  92. package/docs/pages/signetjs/chain-adapters/bitcoin/index.mdx +119 -0
  93. package/docs/pages/signetjs/chain-adapters/bitcoin/prepare-transaction-for-signing.mdx +30 -0
  94. package/docs/pages/signetjs/chain-adapters/broadcast-tx.mdx +23 -0
  95. package/docs/pages/signetjs/chain-adapters/cosmos/finalize-transaction-signing.mdx +53 -0
  96. package/docs/pages/signetjs/chain-adapters/cosmos/index.mdx +108 -0
  97. package/docs/pages/signetjs/chain-adapters/cosmos/prepare-transaction-for-signing.mdx +39 -0
  98. package/docs/pages/signetjs/chain-adapters/derive-address-and-public-key.mdx +28 -0
  99. package/docs/pages/signetjs/chain-adapters/evm/finalize-message-signing.mdx +33 -0
  100. package/docs/pages/signetjs/chain-adapters/evm/finalize-transaction-signing.mdx +44 -0
  101. package/docs/pages/signetjs/chain-adapters/evm/finalize-typed-data-signing.mdx +34 -0
  102. package/docs/pages/signetjs/chain-adapters/evm/index.mdx +84 -0
  103. package/docs/pages/signetjs/chain-adapters/evm/prepare-message-for-signing.mdx +26 -0
  104. package/docs/pages/signetjs/chain-adapters/evm/prepare-transaction-for-signing.mdx +30 -0
  105. package/docs/pages/signetjs/chain-adapters/evm/prepare-typed-data-for-signing.mdx +44 -0
  106. package/docs/pages/signetjs/chain-adapters/get-balance.mdx +26 -0
  107. package/docs/pages/signetjs/contracts/evm/constructor.mdx +38 -0
  108. package/docs/pages/signetjs/contracts/evm/get-current-signature-deposit.mdx +17 -0
  109. package/docs/pages/signetjs/contracts/evm/get-derived-public-key.mdx +28 -0
  110. package/docs/pages/signetjs/contracts/evm/get-latest-key-version.mdx +17 -0
  111. package/docs/pages/signetjs/contracts/evm/get-public-key.mdx +17 -0
  112. package/docs/pages/signetjs/contracts/evm/sign.mdx +36 -0
  113. package/docs/pages/signetjs/contracts/near/constructor.mdx +29 -0
  114. package/docs/pages/signetjs/contracts/near/get-current-signature-deposit.mdx +17 -0
  115. package/docs/pages/signetjs/contracts/near/get-derived-public-key.mdx +28 -0
  116. package/docs/pages/signetjs/contracts/near/get-public-key.mdx +17 -0
  117. package/docs/pages/signetjs/contracts/near/sign.mdx +32 -0
  118. package/docs/public/signet-logo.png +0 -0
  119. package/docs/snippets/code/chains.ts +42 -0
  120. package/docs/snippets/code/contract.ts +44 -0
  121. package/docs/snippets/code/evm/contract.ts +24 -0
  122. package/docs/snippets/code/evm/env.ts +16 -0
  123. package/docs/snippets/code/near/env.ts +13 -0
  124. package/hardhat.config.mts +19 -0
  125. package/package.json +18 -19
  126. package/src/chain-adapters/Bitcoin/BTCRpcAdapter/BTCRpcAdapter.ts +15 -0
  127. package/src/chain-adapters/Bitcoin/BTCRpcAdapter/Mempool/Mempool.ts +101 -0
  128. package/src/chain-adapters/Bitcoin/BTCRpcAdapter/Mempool/index.ts +1 -0
  129. package/src/chain-adapters/Bitcoin/BTCRpcAdapter/Mempool/types.ts +72 -0
  130. package/src/chain-adapters/Bitcoin/BTCRpcAdapter/index.ts +6 -0
  131. package/src/chain-adapters/Bitcoin/Bitcoin.ts +287 -0
  132. package/src/chain-adapters/Bitcoin/index.ts +13 -0
  133. package/src/chain-adapters/Bitcoin/types.ts +48 -0
  134. package/src/chain-adapters/Bitcoin/utils.ts +14 -0
  135. package/src/chain-adapters/ChainAdapter.ts +92 -0
  136. package/src/chain-adapters/Cosmos/Cosmos.ts +258 -0
  137. package/src/chain-adapters/Cosmos/index.ts +8 -0
  138. package/src/chain-adapters/Cosmos/types.ts +35 -0
  139. package/src/chain-adapters/Cosmos/utils.ts +45 -0
  140. package/src/chain-adapters/EVM/EVM.test.ts +238 -0
  141. package/src/chain-adapters/EVM/EVM.ts +337 -0
  142. package/src/chain-adapters/EVM/index.ts +11 -0
  143. package/src/chain-adapters/EVM/types.ts +53 -0
  144. package/src/chain-adapters/EVM/utils.ts +27 -0
  145. package/src/chain-adapters/index.ts +5 -0
  146. package/src/constants.ts +62 -0
  147. package/src/contracts/ChainSignatureContract.ts +65 -0
  148. package/src/contracts/evm/ChainSignaturesContract.ts +323 -0
  149. package/src/contracts/evm/ChainSignaturesContractABI.ts +359 -0
  150. package/src/contracts/evm/errors.ts +52 -0
  151. package/src/contracts/evm/index.ts +10 -0
  152. package/src/contracts/evm/types.ts +39 -0
  153. package/src/contracts/evm/utils.ts +41 -0
  154. package/src/contracts/index.ts +4 -0
  155. package/src/contracts/near/ChainSignatureContract.ts +196 -0
  156. package/src/contracts/near/account.ts +42 -0
  157. package/src/contracts/near/constants.ts +4 -0
  158. package/src/contracts/near/index.ts +10 -0
  159. package/src/contracts/near/signAndSend/index.ts +1 -0
  160. package/src/contracts/near/signAndSend/keypair.ts +178 -0
  161. package/src/contracts/near/transaction.ts +202 -0
  162. package/src/contracts/near/types.ts +71 -0
  163. package/src/index.ts +5 -0
  164. package/src/types.ts +46 -0
  165. package/src/utils/cryptography.ts +141 -0
  166. package/src/utils/index.ts +1 -0
  167. package/src/utils/publicKey.ts +17 -0
  168. package/tsconfig.eslint.json +8 -0
  169. package/tsconfig.json +126 -0
  170. package/tsup.config.ts +58 -0
  171. package/vitest.config.ts +19 -0
  172. package/vocs.config.ts +213 -0
  173. package/browser/index.browser.cjs +0 -3
  174. package/browser/index.browser.cjs.map +0 -1
  175. package/browser/index.browser.js +0 -3
  176. package/browser/index.browser.js.map +0 -1
  177. package/node/index.node.cjs +0 -3
  178. package/node/index.node.cjs.map +0 -1
  179. package/node/index.node.js +0 -3
  180. package/node/index.node.js.map +0 -1
  181. package/types/index.d.cts +0 -1048
  182. package/types/index.d.ts +0 -1048
@@ -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,5 @@
1
+ export { ChainAdapter } from './ChainAdapter'
2
+
3
+ export * as evm from './EVM'
4
+ export * as btc from './Bitcoin'
5
+ export * as cosmos from './Cosmos'
@@ -0,0 +1,62 @@
1
+ import { type NajPublicKey } from '@types'
2
+
3
+ export const ENVS = {
4
+ TESTNET_DEV: 'TESTNET_DEV',
5
+ TESTNET: 'TESTNET',
6
+ MAINNET: 'MAINNET',
7
+ } as const
8
+
9
+ export const CHAINS = {
10
+ ETHEREUM: 'ETHEREUM',
11
+ NEAR: 'NEAR',
12
+ } as const
13
+
14
+ /**
15
+ * Root public keys for the Sig Network Smart Contracts across different environments.
16
+ *
17
+ * These keys should never change.
18
+ */
19
+ export const ROOT_PUBLIC_KEYS: Record<keyof typeof ENVS, NajPublicKey> = {
20
+ [ENVS.TESTNET_DEV]:
21
+ 'secp256k1:54hU5wcCmVUPFWLDALXMh1fFToZsVXrx9BbTbHzSfQq1Kd1rJZi52iPa4QQxo6s5TgjWqgpY8HamYuUDzG6fAaUq',
22
+ [ENVS.TESTNET]:
23
+ 'secp256k1:3Ww8iFjqTHufye5aRGUvrQqETegR4gVUcW8FX5xzscaN9ENhpkffojsxJwi6N1RbbHMTxYa9UyKeqK3fsMuwxjR5',
24
+ [ENVS.MAINNET]:
25
+ 'secp256k1:4tY4qMzusmgX5wYdG35663Y3Qar3CTbpApotwk9ZKLoF79XA4DjG8XoByaKdNHKQX9Lz5hd7iJqsWdTKyA7dKa6Z',
26
+ }
27
+
28
+ /**
29
+ * Chain IDs used in the key derivation function (KDF) for deriving child public keys to
30
+ * distinguish between different chains.
31
+ *
32
+ * @see {@link deriveChildPublicKey} in cryptography.ts for usage details
33
+ */
34
+ export const KDF_CHAIN_IDS = {
35
+ [CHAINS.ETHEREUM]: '0x1',
36
+ [CHAINS.NEAR]: '0x18d',
37
+ } as const
38
+
39
+ /**
40
+ * Contract addresses for different chains and environments.
41
+ *
42
+ * - Testnet Dev: Used for internal development, very unstable
43
+ * - Testnet: Used for external development, stable
44
+ * - Mainnet: Production contract address
45
+ *
46
+ * @see ChainSignatureContract documentation for implementation details
47
+ */
48
+ export const CONTRACT_ADDRESSES: Record<
49
+ keyof typeof CHAINS,
50
+ Record<keyof typeof ENVS, string>
51
+ > = {
52
+ [CHAINS.NEAR]: {
53
+ [ENVS.TESTNET_DEV]: 'dev.sig-net.testnet',
54
+ [ENVS.TESTNET]: 'v1.sig-net.testnet',
55
+ [ENVS.MAINNET]: 'v1.sig-net.near',
56
+ },
57
+ [CHAINS.ETHEREUM]: {
58
+ [ENVS.TESTNET_DEV]: '0x69C6b28Fdc74618817fa380De29a653060e14009',
59
+ [ENVS.TESTNET]: '0x83458E8Bf8206131Fe5c05127007FA164c0948A2',
60
+ [ENVS.MAINNET]: '0xf8bdC0612361a1E49a8E01423d4C0cFc5dF4791A',
61
+ },
62
+ }
@@ -0,0 +1,65 @@
1
+ import type BN from 'bn.js'
2
+
3
+ import type { RSVSignature, UncompressedPubKeySEC1 } from '@types'
4
+
5
+ export interface SignArgs {
6
+ /** The payload to sign as an array of 32 bytes */
7
+ payload: number[]
8
+ /** The derivation path for key generation */
9
+ path: string
10
+ /** Version of the key to use */
11
+ key_version: number
12
+ }
13
+
14
+ /**
15
+ * Base contract interface required for compatibility with ChainAdapter instances like EVM and Bitcoin.
16
+ *
17
+ * See {@link EVM} and {@link Bitcoin} for example implementations.
18
+ */
19
+ export abstract class BaseChainSignatureContract {
20
+ /**
21
+ * Gets the current signature deposit required by the contract.
22
+ * This deposit amount helps manage network congestion.
23
+ *
24
+ * @returns Promise resolving to the required deposit amount as a BigNumber
25
+ */
26
+ abstract getCurrentSignatureDeposit(): Promise<BN>
27
+
28
+ /**
29
+ * Derives a child public key using a\ derivation path and predecessor.
30
+ *
31
+ * @param args - Arguments for key derivation
32
+ * @param args.path - The string path to use derive the key
33
+ * @param args.predecessor - The id/address of the account requesting signature
34
+ * @returns Promise resolving to the derived SEC1 uncompressed public key
35
+ */
36
+ abstract getDerivedPublicKey(
37
+ args: {
38
+ path: string
39
+ predecessor: string
40
+ } & Record<string, unknown>
41
+ ): Promise<UncompressedPubKeySEC1>
42
+ }
43
+
44
+ /**
45
+ * Full contract interface that extends BaseChainSignatureContract to provide all Sig Network Smart Contract capabilities.
46
+ */
47
+ export abstract class ChainSignatureContract extends BaseChainSignatureContract {
48
+ /**
49
+ * Signs a payload using Sig Network MPC.
50
+ *
51
+ * @param args - Arguments for the signing operation
52
+ * @param args.payload - The data to sign as an array of 32 bytes
53
+ * @param args.path - The string path to use derive the key
54
+ * @param args.key_version - Version of the key to use
55
+ * @returns Promise resolving to the RSV signature
56
+ */
57
+ abstract sign(args: SignArgs & Record<string, unknown>): Promise<RSVSignature>
58
+
59
+ /**
60
+ * Gets the public key associated with this contract instance.
61
+ *
62
+ * @returns Promise resolving to the SEC1 uncompressed public key
63
+ */
64
+ abstract getPublicKey(): Promise<UncompressedPubKeySEC1>
65
+ }
@@ -0,0 +1,323 @@
1
+ import { najToUncompressedPubKeySEC1 } from '@utils/cryptography'
2
+ import { getRootPublicKey } from '@utils/publicKey'
3
+ import BN from 'bn.js'
4
+ import { withRetry, type PublicClient, type WalletClient, type Hex } from 'viem'
5
+
6
+ import { CHAINS, KDF_CHAIN_IDS } from '@constants'
7
+ import { ChainSignatureContract as AbstractChainSignatureContract } from '@contracts/ChainSignatureContract'
8
+ import type { SignArgs } from '@contracts/ChainSignatureContract'
9
+ import type {
10
+ NajPublicKey,
11
+ RSVSignature,
12
+ SigNetEvmMpcSignature,
13
+ UncompressedPubKeySEC1,
14
+ } from '@types'
15
+ import { cryptography } from '@utils'
16
+
17
+ import { abi } from './ChainSignaturesContractABI'
18
+ import {
19
+ SignatureNotFoundError,
20
+ SignatureContractError,
21
+ SigningError,
22
+ } from './errors'
23
+ import type {
24
+ RequestIdArgs,
25
+ SignOptions,
26
+ SignRequest,
27
+ SignatureErrorData,
28
+ } from './types'
29
+ import { getRequestId } from './utils'
30
+
31
+ /**
32
+ * Implementation of the ChainSignatureContract for EVM chains.
33
+ *
34
+ * When signing data, the contract emits a SignatureRequested event with a requestId.
35
+ * This requestId is used to track the signature request and retrieve the signature
36
+ * once it's available. The sign method handles this process automatically by polling
37
+ * for the signature using the requestId.
38
+ */
39
+ export class ChainSignatureContract extends AbstractChainSignatureContract {
40
+ private readonly publicClient: PublicClient
41
+ private readonly walletClient: WalletClient
42
+ private readonly contractAddress: Hex
43
+ private readonly rootPublicKey: NajPublicKey
44
+
45
+ /**
46
+ * Creates a new instance of the ChainSignatureContract for EVM chains.
47
+ *
48
+ * @param args - Configuration options for the contract
49
+ * @param args.publicClient - A Viem PublicClient instance for reading from the blockchain
50
+ * @param args.walletClient - A Viem WalletClient instance for sending transactions
51
+ * @param args.contractAddress - The address of the deployed ChainSignatures contract (e.g. `0x857ED3A242B59cC24144814a0DF41C397a3811E6`)
52
+ * @param args.rootPublicKey - Optional root public key. If not provided, it will be derived from the contract address
53
+ */
54
+ constructor(args: {
55
+ publicClient: PublicClient
56
+ walletClient: WalletClient
57
+ contractAddress: Hex
58
+ rootPublicKey?: NajPublicKey
59
+ }) {
60
+ super()
61
+ this.publicClient = args.publicClient
62
+ this.walletClient = args.walletClient
63
+ this.contractAddress = args.contractAddress
64
+
65
+ const rootPublicKey =
66
+ args.rootPublicKey ||
67
+ getRootPublicKey(this.contractAddress, CHAINS.ETHEREUM)
68
+
69
+ if (!rootPublicKey) {
70
+ throw new Error(
71
+ `Invalid public key, please provide a valid root public key or contract address`
72
+ )
73
+ }
74
+
75
+ this.rootPublicKey = rootPublicKey
76
+ }
77
+
78
+ async getCurrentSignatureDeposit(): Promise<BN> {
79
+ const deposit = (await this.publicClient.readContract({
80
+ address: this.contractAddress,
81
+ abi,
82
+ functionName: 'getSignatureDeposit',
83
+ })) as bigint
84
+
85
+ return new BN(deposit.toString())
86
+ }
87
+
88
+ async getDerivedPublicKey(args: {
89
+ path: string
90
+ predecessor: string
91
+ }): Promise<UncompressedPubKeySEC1> {
92
+ const pubKey = cryptography.deriveChildPublicKey(
93
+ await this.getPublicKey(),
94
+ args.predecessor.toLowerCase(),
95
+ args.path,
96
+ KDF_CHAIN_IDS.ETHEREUM
97
+ )
98
+
99
+ return pubKey
100
+ }
101
+
102
+ async getPublicKey(): Promise<UncompressedPubKeySEC1> {
103
+ return najToUncompressedPubKeySEC1(this.rootPublicKey)
104
+ }
105
+
106
+ async getLatestKeyVersion(): Promise<number> {
107
+ const version = (await this.publicClient.readContract({
108
+ address: this.contractAddress,
109
+ abi,
110
+ functionName: 'latestKeyVersion',
111
+ })) as bigint
112
+
113
+ return Number(version)
114
+ }
115
+
116
+ /**
117
+ * Sends a transaction to the contract to request a signature, then
118
+ * polls for the signature result. If the signature is not found within the retry
119
+ * parameters, it will throw an error.
120
+ */
121
+ async sign(
122
+ args: SignArgs,
123
+ options: SignOptions = {
124
+ sign: {
125
+ algo: '',
126
+ dest: '',
127
+ params: '',
128
+ },
129
+ retry: {
130
+ delay: 5000,
131
+ retryCount: 12,
132
+ },
133
+ }
134
+ ): Promise<RSVSignature> {
135
+ if (!this.walletClient?.account) {
136
+ throw new Error('Wallet client required for signing operations')
137
+ }
138
+
139
+ const request: SignRequest = {
140
+ payload: `0x${Buffer.from(args.payload).toString('hex')}`,
141
+ path: args.path,
142
+ keyVersion: args.key_version,
143
+ algo: options.sign.algo ?? '',
144
+ dest: options.sign.dest ?? '',
145
+ params: options.sign.params ?? '',
146
+ }
147
+
148
+ const requestId = this.getRequestId({
149
+ ...request,
150
+ address: this.walletClient.account.address,
151
+ chainId: this.publicClient.chain?.id
152
+ ? BigInt(this.publicClient.chain.id)
153
+ : 0n,
154
+ })
155
+
156
+ const hash = await this.walletClient.writeContract({
157
+ address: this.contractAddress,
158
+ abi,
159
+ chain: this.publicClient.chain,
160
+ account: this.walletClient.account,
161
+ functionName: 'sign',
162
+ args: [request],
163
+ value: BigInt((await this.getCurrentSignatureDeposit()).toString()),
164
+ })
165
+
166
+ const receipt = await this.publicClient.waitForTransactionReceipt({ hash })
167
+
168
+ try {
169
+ const result = await withRetry(
170
+ async () => {
171
+ const result = await this.getSignatureFromEvents(
172
+ requestId,
173
+ receipt.blockNumber
174
+ )
175
+
176
+ // TODO: Validate if this is the signature corresponding to the transaction as anybody can call respond on the contract
177
+
178
+ if (result) {
179
+ return result
180
+ } else {
181
+ throw new Error('Signature not found yet')
182
+ }
183
+ },
184
+ {
185
+ delay: options.retry.delay,
186
+ retryCount: options.retry.retryCount,
187
+ shouldRetry: ({ count, error }) => {
188
+ // TODO: Should be enabled only on debug mode
189
+ console.log(
190
+ `Retrying get signature: ${count}/${options.retry.retryCount}`
191
+ )
192
+ return error.message === 'Signature not found yet'
193
+ },
194
+ }
195
+ )
196
+
197
+ if (result) {
198
+ return result
199
+ } else {
200
+ const errorData = await this.getErrorFromEvents(
201
+ requestId,
202
+ receipt.blockNumber
203
+ )
204
+ if (errorData) {
205
+ throw new SignatureContractError(errorData.error, requestId, receipt)
206
+ } else {
207
+ throw new SignatureNotFoundError(requestId, receipt)
208
+ }
209
+ }
210
+ } catch (error) {
211
+ if (
212
+ error instanceof SignatureNotFoundError ||
213
+ error instanceof SignatureContractError
214
+ ) {
215
+ throw error
216
+ } else {
217
+ throw new SigningError(
218
+ requestId,
219
+ receipt,
220
+ error instanceof Error ? error : undefined
221
+ )
222
+ }
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Generates the request ID for a signature request allowing to track the response.
228
+ *
229
+ * @param request - The signature request object containing:
230
+ * @param request.address - The contract/wallet address calling the signing contract
231
+ * @param request.payload - The data payload to be signed as a hex string
232
+ * @param request.path - The derivation path for the key
233
+ * @param request.keyVersion - The version of the key to use
234
+ * @param request.chainId - The chain ID as a bigint
235
+ * @param request.algo - The signing algorithm to use
236
+ * @param request.dest - The destination for the signature
237
+ * @param request.params - Additional parameters for the signing process
238
+ * @returns A hex string representing the unique request ID
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * const requestId = ChainSignatureContract.getRequestId({
243
+ * address: walletClient.account.address,
244
+ * payload: payload: `0x${Buffer.from(args.payload).toString('hex')}`,,
245
+ * path: '',
246
+ * keyVersion: 0,
247
+ * chainId: 1n,
248
+ * algo: '',
249
+ * dest: '',
250
+ * params: ''
251
+ * });
252
+ * console.log(requestId); // 0x...
253
+ * ```
254
+ */
255
+ getRequestId(request: RequestIdArgs): Hex {
256
+ return getRequestId(request)
257
+ }
258
+
259
+ async getErrorFromEvents(
260
+ requestId: Hex,
261
+ fromBlock: bigint
262
+ ): Promise<SignatureErrorData | undefined> {
263
+ const errorLogs = await this.publicClient.getContractEvents({
264
+ address: this.contractAddress,
265
+ abi,
266
+ eventName: 'SignatureError',
267
+ args: {
268
+ requestId,
269
+ },
270
+ fromBlock,
271
+ toBlock: 'latest',
272
+ })
273
+
274
+ if (errorLogs.length > 0) {
275
+ const { args: errorData } = errorLogs[
276
+ errorLogs.length - 1
277
+ ] as unknown as {
278
+ args: SignatureErrorData
279
+ }
280
+
281
+ return errorData
282
+ }
283
+
284
+ return undefined
285
+ }
286
+
287
+ /**
288
+ * Searches for SignatureResponded events that match the given requestId.
289
+ * It works in conjunction with the getRequestId method which generates the unique
290
+ * identifier for a signature request.
291
+ *
292
+ * @param requestId - The identifier for the signature request
293
+ * @param fromBlock - The block number to start searching from
294
+ * @returns The RSV signature if found, undefined otherwise
295
+ */
296
+ async getSignatureFromEvents(
297
+ requestId: Hex,
298
+ fromBlock: bigint
299
+ ): Promise<RSVSignature | undefined> {
300
+ const logs = await this.publicClient.getContractEvents({
301
+ address: this.contractAddress,
302
+ abi,
303
+ eventName: 'SignatureResponded',
304
+ args: {
305
+ requestId,
306
+ },
307
+ fromBlock,
308
+ toBlock: 'latest',
309
+ })
310
+
311
+ if (logs.length > 0) {
312
+ const { args: signatureData } = logs[logs.length - 1] as unknown as {
313
+ args: {
314
+ signature: SigNetEvmMpcSignature
315
+ }
316
+ }
317
+
318
+ return cryptography.toRSV(signatureData.signature)
319
+ }
320
+
321
+ return undefined
322
+ }
323
+ }