signet.js 0.0.1-beta.6 → 0.0.1-beta.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 (121) hide show
  1. package/.eslintrc.json +55 -0
  2. package/.prettierrc +1 -0
  3. package/LICENSE +19 -0
  4. package/README.md +42 -14
  5. package/babel.config.js +6 -0
  6. package/docs/pages/chain-signatures-contract.mdx +56 -0
  7. package/docs/pages/chain.mdx +45 -0
  8. package/docs/pages/chains/bitcoin/bitcoin.mdx +191 -0
  9. package/docs/pages/chains/bitcoin/btc-rpc-adapter.mdx +307 -0
  10. package/docs/pages/chains/cosmos.mdx +181 -0
  11. package/docs/pages/chains/evm.mdx +189 -0
  12. package/docs/pages/index.mdx +99 -0
  13. package/docs/snippets/contract.ts +21 -0
  14. package/docs/snippets/env.ts +13 -0
  15. package/jest.config.ts +199 -0
  16. package/package.json +21 -11
  17. package/src/chains/Bitcoin/BTCRpcAdapter/BTCRpcAdapter.ts +11 -0
  18. package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/Mempool.ts +96 -0
  19. package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/index.ts +1 -0
  20. package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/types.ts +72 -0
  21. package/src/chains/Bitcoin/BTCRpcAdapter/index.ts +6 -0
  22. package/src/chains/Bitcoin/Bitcoin.ts +301 -0
  23. package/src/chains/Bitcoin/types.ts +45 -0
  24. package/src/chains/Bitcoin/utils.ts +14 -0
  25. package/src/chains/Chain.ts +96 -0
  26. package/src/chains/ChainSignatureContract.ts +48 -0
  27. package/src/chains/Cosmos/Cosmos.ts +279 -0
  28. package/src/chains/Cosmos/types.ts +35 -0
  29. package/src/chains/Cosmos/utils.ts +45 -0
  30. package/src/chains/EVM/EVM.ts +180 -0
  31. package/src/chains/EVM/types.ts +7 -0
  32. package/src/chains/EVM/utils.ts +26 -0
  33. package/src/chains/index.ts +34 -0
  34. package/src/chains/types.ts +35 -0
  35. package/src/chains/utils.ts +40 -0
  36. package/src/index.ts +2 -0
  37. package/src/utils/chains/index.ts +1 -0
  38. package/src/utils/chains/near/ChainSignatureContract.ts +195 -0
  39. package/src/utils/chains/near/account.ts +42 -0
  40. package/src/utils/chains/near/constants.ts +4 -0
  41. package/src/utils/chains/near/index.ts +3 -0
  42. package/src/utils/chains/near/relayer/index.ts +1 -0
  43. package/src/utils/chains/near/relayer/relayer.ts +39 -0
  44. package/src/utils/chains/near/relayer/types.ts +24 -0
  45. package/src/utils/chains/near/signAndSend/index.ts +1 -0
  46. package/src/utils/chains/near/signAndSend/keypair.ts +180 -0
  47. package/src/utils/chains/near/transactionBuilder.ts +138 -0
  48. package/src/utils/chains/near/types.ts +67 -0
  49. package/src/utils/index.ts +1 -0
  50. package/tsconfig.eslint.json +8 -0
  51. package/tsconfig.json +116 -0
  52. package/vite.config.ts +47 -0
  53. package/vocs.config.ts +60 -0
  54. package/src/chains/Bitcoin/BTCRpcAdapter/BTCRpcAdapter.d.ts +0 -10
  55. package/src/chains/Bitcoin/BTCRpcAdapter/BTCRpcAdapter.js +0 -2
  56. package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/Mempool.d.ts +0 -15
  57. package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/Mempool.js +0 -69
  58. package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/index.d.ts +0 -1
  59. package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/index.js +0 -1
  60. package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/types.d.ts +0 -69
  61. package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/types.js +0 -1
  62. package/src/chains/Bitcoin/BTCRpcAdapter/index.d.ts +0 -5
  63. package/src/chains/Bitcoin/BTCRpcAdapter/index.js +0 -5
  64. package/src/chains/Bitcoin/Bitcoin.d.ts +0 -69
  65. package/src/chains/Bitcoin/Bitcoin.js +0 -198
  66. package/src/chains/Bitcoin/types.d.ts +0 -42
  67. package/src/chains/Bitcoin/types.js +0 -1
  68. package/src/chains/Bitcoin/utils.d.ts +0 -2
  69. package/src/chains/Bitcoin/utils.js +0 -13
  70. package/src/chains/Chain.d.ts +0 -89
  71. package/src/chains/Chain.js +0 -9
  72. package/src/chains/ChainSignatureContract.d.ts +0 -62
  73. package/src/chains/ChainSignatureContract.js +0 -7
  74. package/src/chains/Cosmos/Cosmos.d.ts +0 -51
  75. package/src/chains/Cosmos/Cosmos.js +0 -157
  76. package/src/chains/Cosmos/types.d.ts +0 -30
  77. package/src/chains/Cosmos/types.js +0 -1
  78. package/src/chains/Cosmos/utils.d.ts +0 -2
  79. package/src/chains/Cosmos/utils.js +0 -27
  80. package/src/chains/EVM/EVM.d.ts +0 -42
  81. package/src/chains/EVM/EVM.js +0 -109
  82. package/src/chains/EVM/types.d.ts +0 -5
  83. package/src/chains/EVM/types.js +0 -1
  84. package/src/chains/EVM/utils.d.ts +0 -7
  85. package/src/chains/EVM/utils.js +0 -14
  86. package/src/chains/index.d.ts +0 -12
  87. package/src/chains/index.js +0 -12
  88. package/src/chains/types.d.ts +0 -31
  89. package/src/chains/types.js +0 -1
  90. package/src/chains/utils.d.ts +0 -12
  91. package/src/chains/utils.js +0 -27
  92. package/src/index.d.ts +0 -2
  93. package/src/index.js +0 -2
  94. package/src/utils/chains/index.d.ts +0 -1
  95. package/src/utils/chains/index.js +0 -1
  96. package/src/utils/chains/near/account.d.ts +0 -13
  97. package/src/utils/chains/near/account.js +0 -22
  98. package/src/utils/chains/near/constants.d.ts +0 -3
  99. package/src/utils/chains/near/constants.js +0 -3
  100. package/src/utils/chains/near/contract.d.ts +0 -40
  101. package/src/utils/chains/near/contract.js +0 -102
  102. package/src/utils/chains/near/index.d.ts +0 -3
  103. package/src/utils/chains/near/index.js +0 -3
  104. package/src/utils/chains/near/relayer/index.d.ts +0 -1
  105. package/src/utils/chains/near/relayer/index.js +0 -1
  106. package/src/utils/chains/near/relayer/relayer.d.ts +0 -8
  107. package/src/utils/chains/near/relayer/relayer.js +0 -33
  108. package/src/utils/chains/near/relayer/types.d.ts +0 -22
  109. package/src/utils/chains/near/relayer/types.js +0 -1
  110. package/src/utils/chains/near/signAndSend/index.d.ts +0 -1
  111. package/src/utils/chains/near/signAndSend/index.js +0 -1
  112. package/src/utils/chains/near/signAndSend/keypair.d.ts +0 -6
  113. package/src/utils/chains/near/signAndSend/keypair.js +0 -127
  114. package/src/utils/chains/near/transactionBuilder.d.ts +0 -26
  115. package/src/utils/chains/near/transactionBuilder.js +0 -72
  116. package/src/utils/chains/near/types.d.ts +0 -50
  117. package/src/utils/chains/near/types.js +0 -1
  118. package/src/utils/index.d.ts +0 -1
  119. package/src/utils/index.js +0 -1
  120. package/vocs.config.d.ts +0 -3
  121. package/vocs.config.js +0 -71
@@ -0,0 +1,279 @@
1
+ import { encodeSecp256k1Pubkey } from '@cosmjs/amino'
2
+ import { ripemd160, sha256 } from '@cosmjs/crypto'
3
+ import { toBase64, fromBase64, fromHex } from '@cosmjs/encoding'
4
+ import {
5
+ Registry,
6
+ makeSignBytes,
7
+ encodePubkey,
8
+ makeAuthInfoBytes,
9
+ makeSignDoc,
10
+ type TxBodyEncodeObject,
11
+ } from '@cosmjs/proto-signing'
12
+ import { GasPrice, StargateClient, calculateFee } from '@cosmjs/stargate'
13
+ import { bech32 } from 'bech32'
14
+ import { SignMode } from 'cosmjs-types/cosmos/tx/signing/v1beta1/signing'
15
+ import { TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx'
16
+
17
+ import type {
18
+ CosmosNetworkIds,
19
+ CosmosTransactionRequest,
20
+ CosmosUnsignedTransaction,
21
+ } from '@chains/Cosmos/types'
22
+ import type { ChainSignatureContract } from '@chains/ChainSignatureContract'
23
+ import type {
24
+ MPCPayloads,
25
+ RSVSignature,
26
+ KeyDerivationPath,
27
+ } from '@chains/types'
28
+ import { Chain } from '@chains/Chain'
29
+ import { utils } from '@chains'
30
+ import type { ChainInfo, BalanceResponse } from '@chains/Cosmos/types'
31
+ import { fetchChainInfo } from '@chains/Cosmos/utils'
32
+
33
+ /**
34
+ * Implementation of the Chain interface for Cosmos-based networks.
35
+ * Handles interactions with Cosmos SDK chains like Cosmos Hub, Osmosis, etc.
36
+ */
37
+ export class Cosmos extends Chain<
38
+ CosmosTransactionRequest,
39
+ CosmosUnsignedTransaction
40
+ > {
41
+ private readonly registry: Registry
42
+ private readonly chainId: CosmosNetworkIds
43
+ private readonly contract: ChainSignatureContract
44
+ private readonly endpoints?: {
45
+ rpcUrl?: string
46
+ restUrl?: string
47
+ }
48
+
49
+ /**
50
+ * Creates a new Cosmos chain instance
51
+ * @param params - Configuration parameters
52
+ * @param params.chainId - Chain id for the Cosmos network
53
+ * @param params.contract - Instance of the chain signature contract for MPC operations
54
+ * @param params.endpoints - Optional RPC and REST endpoints
55
+ * @param params.endpoints.rpcUrl - Optional RPC endpoint URL
56
+ * @param params.endpoints.restUrl - Optional REST endpoint URL
57
+ */
58
+ constructor({
59
+ chainId,
60
+ contract,
61
+ endpoints,
62
+ }: {
63
+ contract: ChainSignatureContract
64
+ chainId: CosmosNetworkIds
65
+ endpoints?: {
66
+ rpcUrl?: string
67
+ restUrl?: string
68
+ }
69
+ }) {
70
+ super()
71
+
72
+ this.contract = contract
73
+ this.registry = new Registry()
74
+ this.chainId = chainId
75
+ this.endpoints = endpoints
76
+ }
77
+
78
+ private parseRSVSignature(rsvSignature: RSVSignature): Uint8Array {
79
+ return new Uint8Array([
80
+ ...fromHex(rsvSignature.r),
81
+ ...fromHex(rsvSignature.s),
82
+ ])
83
+ }
84
+
85
+ private async getChainInfo(): Promise<ChainInfo> {
86
+ return {
87
+ ...(await fetchChainInfo(this.chainId)),
88
+ ...this.endpoints,
89
+ }
90
+ }
91
+
92
+ async getBalance(address: string): Promise<string> {
93
+ try {
94
+ const { restUrl, denom, decimals } = await this.getChainInfo()
95
+
96
+ const response = await fetch(
97
+ `${restUrl}/cosmos/bank/v1beta1/balances/${address}`
98
+ )
99
+
100
+ if (!response.ok) {
101
+ throw new Error(`HTTP error! status: ${response.status}`)
102
+ }
103
+
104
+ const data = (await response.json()) as BalanceResponse
105
+ const balance = data.balances.find((b) => b.denom === denom)
106
+ const amount = balance?.amount ?? '0'
107
+
108
+ const formattedBalance = (
109
+ parseInt(amount) / Math.pow(10, decimals)
110
+ ).toString()
111
+ return formattedBalance
112
+ } catch (error) {
113
+ console.error('Failed to fetch Cosmos balance:', error)
114
+ throw new Error('Failed to fetch Cosmos balance')
115
+ }
116
+ }
117
+
118
+ async deriveAddressAndPublicKey(
119
+ predecessor: string,
120
+ path: KeyDerivationPath
121
+ ): Promise<{
122
+ address: string
123
+ publicKey: string
124
+ }> {
125
+ const { prefix } = await this.getChainInfo()
126
+ const uncompressedPubKey = await this.contract.getDerivedPublicKey({
127
+ path,
128
+ predecessor,
129
+ })
130
+
131
+ if (!uncompressedPubKey) {
132
+ throw new Error('Failed to get derived public key')
133
+ }
134
+
135
+ const derivedKey = utils.compressPubKey(uncompressedPubKey)
136
+ const pubKeySha256 = sha256(fromHex(derivedKey))
137
+ const ripemd160Hash = ripemd160(pubKeySha256)
138
+ const address = bech32.encode(prefix, bech32.toWords(ripemd160Hash))
139
+
140
+ return { address, publicKey: derivedKey }
141
+ }
142
+
143
+ setTransaction(
144
+ transaction: CosmosUnsignedTransaction,
145
+ storageKey: string
146
+ ): void {
147
+ const serialized = TxRaw.encode(transaction).finish()
148
+ window.localStorage.setItem(storageKey, toBase64(serialized))
149
+ }
150
+
151
+ getTransaction(
152
+ storageKey: string,
153
+ options?: {
154
+ remove?: boolean
155
+ }
156
+ ): CosmosUnsignedTransaction | undefined {
157
+ const serialized = window.localStorage.getItem(storageKey)
158
+ if (!serialized) return undefined
159
+
160
+ if (options?.remove) {
161
+ window.localStorage.removeItem(storageKey)
162
+ }
163
+
164
+ return TxRaw.decode(fromBase64(serialized))
165
+ }
166
+
167
+ async getMPCPayloadAndTransaction(
168
+ transactionRequest: CosmosTransactionRequest
169
+ ): Promise<{
170
+ transaction: CosmosUnsignedTransaction
171
+ mpcPayloads: MPCPayloads
172
+ }> {
173
+ const { denom, rpcUrl, gasPrice } = await this.getChainInfo()
174
+ const publicKeyBytes = fromHex(transactionRequest.publicKey)
175
+
176
+ const gasLimit = transactionRequest.gas || 200_000
177
+
178
+ const fee = calculateFee(
179
+ gasLimit,
180
+ GasPrice.fromString(`${gasPrice}${denom}`)
181
+ )
182
+
183
+ const client = await StargateClient.connect(rpcUrl)
184
+ const accountOnChain = await client.getAccount(transactionRequest.address)
185
+ if (!accountOnChain) {
186
+ throw new Error(
187
+ `Account ${transactionRequest.address} does not exist on chain`
188
+ )
189
+ }
190
+
191
+ const { accountNumber, sequence } = accountOnChain
192
+
193
+ const txBodyEncodeObject: TxBodyEncodeObject = {
194
+ typeUrl: '/cosmos.tx.v1beta1.TxBody',
195
+ value: {
196
+ messages: transactionRequest.messages,
197
+ memo: transactionRequest.memo || '',
198
+ },
199
+ }
200
+
201
+ const txBodyBytes = this.registry.encode(txBodyEncodeObject)
202
+
203
+ const pubkey = encodePubkey(encodeSecp256k1Pubkey(publicKeyBytes))
204
+
205
+ // TODO: Allow caller to provide: multiple signers, fee payer, fee granter
206
+ const authInfoBytes = makeAuthInfoBytes(
207
+ [
208
+ {
209
+ pubkey,
210
+ sequence,
211
+ },
212
+ ],
213
+ fee.amount,
214
+ Number(fee.gas),
215
+ undefined,
216
+ undefined,
217
+ SignMode.SIGN_MODE_DIRECT
218
+ )
219
+
220
+ const signDoc = makeSignDoc(
221
+ txBodyBytes,
222
+ authInfoBytes,
223
+ this.chainId,
224
+ accountNumber
225
+ )
226
+
227
+ const signBytes = makeSignBytes(signDoc)
228
+ const payload = Array.from(sha256(signBytes))
229
+
230
+ return {
231
+ transaction: TxRaw.fromPartial({
232
+ bodyBytes: txBodyBytes,
233
+ authInfoBytes,
234
+ signatures: [],
235
+ }),
236
+ mpcPayloads: [
237
+ {
238
+ index: 0,
239
+ payload,
240
+ },
241
+ ],
242
+ }
243
+ }
244
+
245
+ addSignature({
246
+ transaction,
247
+ mpcSignatures,
248
+ }: {
249
+ transaction: CosmosUnsignedTransaction
250
+ mpcSignatures: RSVSignature[]
251
+ }): string {
252
+ // Allow support for multi-sig but the package only supports single-sig
253
+ transaction.signatures = mpcSignatures.map((sig) =>
254
+ this.parseRSVSignature(sig)
255
+ )
256
+
257
+ const txBytes = TxRaw.encode(transaction).finish()
258
+ return Buffer.from(txBytes).toString('hex')
259
+ }
260
+
261
+ async broadcastTx(txSerialized: string): Promise<string> {
262
+ try {
263
+ const { rpcUrl } = await this.getChainInfo()
264
+ const client = await StargateClient.connect(rpcUrl)
265
+
266
+ const txBytes = fromHex(txSerialized)
267
+ const broadcastResponse = await client.broadcastTx(txBytes)
268
+
269
+ if (broadcastResponse.code !== 0) {
270
+ throw new Error(`Broadcast error: ${broadcastResponse.rawLog}`)
271
+ }
272
+
273
+ return broadcastResponse.transactionHash
274
+ } catch (error) {
275
+ console.error('Transaction broadcast failed:', error)
276
+ throw new Error('Failed to broadcast transaction.')
277
+ }
278
+ }
279
+ }
@@ -0,0 +1,35 @@
1
+ import { type EncodeObject } from '@cosmjs/proto-signing'
2
+ import { type TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx'
3
+
4
+ export type CosmosNetworkIds = string
5
+
6
+ export type CosmosUnsignedTransaction = TxRaw
7
+
8
+ export interface CosmosTransactionRequest {
9
+ address: string
10
+ publicKey: string
11
+ messages: EncodeObject[]
12
+ memo?: string
13
+ gas?: number
14
+ }
15
+
16
+ export interface BalanceResponse {
17
+ balances: Array<{
18
+ denom: string
19
+ amount: string
20
+ }>
21
+ pagination: {
22
+ next_key: string | null
23
+ total: string
24
+ }
25
+ }
26
+
27
+ export interface ChainInfo {
28
+ prefix: string
29
+ denom: string
30
+ rpcUrl: string
31
+ restUrl: string
32
+ expectedChainId: string
33
+ gasPrice: number
34
+ decimals: number
35
+ }
@@ -0,0 +1,45 @@
1
+ import { chains, assets } from 'chain-registry'
2
+
3
+ import { type ChainInfo } from '@chains/Cosmos/types'
4
+
5
+ export const fetchChainInfo = async (chainId: string): Promise<ChainInfo> => {
6
+ const chainInfo = chains.find((chain) => chain.chain_id === chainId)
7
+ if (!chainInfo) {
8
+ throw new Error(`Chain info not found for chainId: ${chainId}`)
9
+ }
10
+
11
+ const { bech32_prefix: prefix, chain_id: expectedChainId } = chainInfo
12
+ const denom = chainInfo.staking?.staking_tokens?.[0]?.denom
13
+ const rpcUrl = chainInfo.apis?.rpc?.[0]?.address
14
+ const restUrl = chainInfo.apis?.rest?.[0]?.address
15
+ const gasPrice = chainInfo.fees?.fee_tokens?.[0]?.average_gas_price
16
+
17
+ if (
18
+ !prefix ||
19
+ !denom ||
20
+ !rpcUrl ||
21
+ !restUrl ||
22
+ !expectedChainId ||
23
+ gasPrice === undefined
24
+ ) {
25
+ throw new Error(
26
+ `Missing required chain information for ${chainInfo.chain_name}`
27
+ )
28
+ }
29
+
30
+ const assetList = assets.find(
31
+ (asset) => asset.chain_name === chainInfo.chain_name
32
+ )
33
+ const asset = assetList?.assets.find((asset) => asset.base === denom)
34
+ const decimals = asset?.denom_units.find(
35
+ (unit) => unit.denom === asset.display
36
+ )?.exponent
37
+
38
+ if (decimals === undefined) {
39
+ throw new Error(
40
+ `Could not find decimals for ${denom} on chain ${chainInfo.chain_name}`
41
+ )
42
+ }
43
+
44
+ return { prefix, denom, rpcUrl, restUrl, expectedChainId, gasPrice, decimals }
45
+ }
@@ -0,0 +1,180 @@
1
+ import { fromHex } from '@cosmjs/encoding'
2
+ import { ethers, keccak256 } from 'ethers'
3
+
4
+ import { Chain } from '@chains/Chain'
5
+ import type { ChainSignatureContract } from '@chains/ChainSignatureContract'
6
+ import type {
7
+ EVMTransactionRequest,
8
+ EVMUnsignedTransaction,
9
+ } from '@chains/EVM/types'
10
+ import { fetchEVMFeeProperties } from '@chains/EVM/utils'
11
+ import type {
12
+ MPCPayloads,
13
+ RSVSignature,
14
+ KeyDerivationPath,
15
+ } from '@chains/types'
16
+
17
+ /**
18
+ * Implementation of the Chain interface for EVM-compatible networks.
19
+ * Handles interactions with Ethereum Virtual Machine based blockchains like Ethereum, BSC, Polygon, etc.
20
+ */
21
+ export class EVM extends Chain<EVMTransactionRequest, EVMUnsignedTransaction> {
22
+ private readonly provider: ethers.JsonRpcProvider
23
+ private readonly contract: ChainSignatureContract
24
+
25
+ /**
26
+ * Creates a new EVM chain instance
27
+ * @param params - Configuration parameters
28
+ * @param params.rpcUrl - URL of the EVM JSON-RPC provider (e.g., Infura endpoint)
29
+ * @param params.contract - Instance of the chain signature contract for MPC operations
30
+ */
31
+ constructor({
32
+ rpcUrl,
33
+ contract,
34
+ }: {
35
+ rpcUrl: string
36
+ contract: ChainSignatureContract
37
+ }) {
38
+ super()
39
+
40
+ this.contract = contract
41
+ this.provider = new ethers.JsonRpcProvider(rpcUrl)
42
+ }
43
+
44
+ private async attachGasAndNonce(
45
+ transaction: EVMTransactionRequest
46
+ ): Promise<EVMUnsignedTransaction> {
47
+ const fees = await fetchEVMFeeProperties(
48
+ this.provider._getConnection().url,
49
+ transaction
50
+ )
51
+ const nonce = await this.provider.getTransactionCount(
52
+ transaction.from,
53
+ 'latest'
54
+ )
55
+
56
+ const { from, ...rest } = transaction
57
+
58
+ return {
59
+ ...fees,
60
+ chainId: this.provider._network.chainId,
61
+ nonce,
62
+ type: 2,
63
+ ...rest,
64
+ }
65
+ }
66
+
67
+ private parseSignature(signature: RSVSignature): ethers.SignatureLike {
68
+ return ethers.Signature.from({
69
+ r: `0x${signature.r}`,
70
+ s: `0x${signature.s}`,
71
+ v: signature.v,
72
+ })
73
+ }
74
+
75
+ async deriveAddressAndPublicKey(
76
+ predecessor: string,
77
+ path: KeyDerivationPath
78
+ ): Promise<{
79
+ address: string
80
+ publicKey: string
81
+ }> {
82
+ const uncompressedPubKey = await this.contract.getDerivedPublicKey({
83
+ path,
84
+ predecessor,
85
+ })
86
+
87
+ if (!uncompressedPubKey) {
88
+ throw new Error('Failed to get derived public key')
89
+ }
90
+
91
+ const publicKeyNoPrefix = uncompressedPubKey.startsWith('04')
92
+ ? uncompressedPubKey.substring(2)
93
+ : uncompressedPubKey
94
+
95
+ const hash = ethers.keccak256(fromHex(publicKeyNoPrefix))
96
+
97
+ return {
98
+ address: `0x${hash.substring(hash.length - 40)}`,
99
+ publicKey: uncompressedPubKey,
100
+ }
101
+ }
102
+
103
+ async getBalance(address: string): Promise<string> {
104
+ try {
105
+ const balance = await this.provider.getBalance(address)
106
+ return ethers.formatEther(balance)
107
+ } catch (error) {
108
+ console.error(`Failed to fetch balance for address ${address}:`, error)
109
+ throw new Error('Failed to fetch balance.')
110
+ }
111
+ }
112
+
113
+ setTransaction(
114
+ transaction: EVMUnsignedTransaction,
115
+ storageKey: string
116
+ ): void {
117
+ const serializedTransaction = JSON.stringify(transaction, (_, value) =>
118
+ typeof value === 'bigint' ? value.toString() : value
119
+ )
120
+ window.localStorage.setItem(storageKey, serializedTransaction)
121
+ }
122
+
123
+ getTransaction(
124
+ storageKey: string,
125
+ options?: {
126
+ remove?: boolean
127
+ }
128
+ ): EVMUnsignedTransaction | undefined {
129
+ const txSerialized = window.localStorage.getItem(storageKey)
130
+ if (options?.remove) {
131
+ window.localStorage.removeItem(storageKey)
132
+ }
133
+ return txSerialized ? JSON.parse(txSerialized) : undefined
134
+ }
135
+
136
+ async getMPCPayloadAndTransaction(
137
+ transactionRequest: EVMTransactionRequest
138
+ ): Promise<{
139
+ transaction: EVMUnsignedTransaction
140
+ mpcPayloads: MPCPayloads
141
+ }> {
142
+ const transaction = await this.attachGasAndNonce(transactionRequest)
143
+ const txSerialized = ethers.Transaction.from(transaction).unsignedSerialized
144
+ const transactionHash = keccak256(txSerialized)
145
+ const txHash = Array.from(ethers.getBytes(transactionHash))
146
+
147
+ return {
148
+ transaction,
149
+ mpcPayloads: [
150
+ {
151
+ index: 0,
152
+ payload: txHash,
153
+ },
154
+ ],
155
+ }
156
+ }
157
+
158
+ addSignature({
159
+ transaction,
160
+ mpcSignatures,
161
+ }: {
162
+ transaction: EVMUnsignedTransaction
163
+ mpcSignatures: RSVSignature[]
164
+ }): string {
165
+ return ethers.Transaction.from({
166
+ ...transaction,
167
+ signature: this.parseSignature(mpcSignatures[0]),
168
+ }).serialized
169
+ }
170
+
171
+ async broadcastTx(txSerialized: string): Promise<string> {
172
+ try {
173
+ const txResponse = await this.provider.broadcastTransaction(txSerialized)
174
+ return txResponse.hash
175
+ } catch (error) {
176
+ console.error('Transaction broadcast failed:', error)
177
+ throw new Error('Failed to broadcast transaction.')
178
+ }
179
+ }
180
+ }
@@ -0,0 +1,7 @@
1
+ import type * as ethers from 'ethers'
2
+
3
+ export type EVMUnsignedTransaction = ethers.TransactionLike
4
+
5
+ export type EVMTransactionRequest = Omit<ethers.TransactionLike, 'from'> & {
6
+ from: string
7
+ }
@@ -0,0 +1,26 @@
1
+ import { ethers } from 'ethers'
2
+
3
+ export async function fetchEVMFeeProperties(
4
+ providerUrl: string,
5
+ transaction: ethers.TransactionLike
6
+ ): Promise<{
7
+ gasLimit: bigint
8
+ maxFeePerGas: bigint
9
+ maxPriorityFeePerGas: bigint
10
+ maxFee: bigint
11
+ }> {
12
+ const provider = new ethers.JsonRpcProvider(providerUrl)
13
+ const gasLimit = await provider.estimateGas(transaction)
14
+ const feeData = await provider.getFeeData()
15
+
16
+ const maxFeePerGas = feeData.maxFeePerGas ?? ethers.parseUnits('10', 'gwei')
17
+ const maxPriorityFeePerGas =
18
+ feeData.maxPriorityFeePerGas ?? ethers.parseUnits('10', 'gwei')
19
+
20
+ return {
21
+ gasLimit,
22
+ maxFeePerGas,
23
+ maxPriorityFeePerGas,
24
+ maxFee: maxFeePerGas * gasLimit,
25
+ }
26
+ }
@@ -0,0 +1,34 @@
1
+ export { Chain } from './Chain'
2
+ export { ChainSignatureContract, type SignArgs } from './ChainSignatureContract'
3
+ export * from './types'
4
+ export * as utils from './utils'
5
+
6
+ // EVM
7
+ export { EVM } from './EVM/EVM'
8
+
9
+ export { fetchEVMFeeProperties } from './EVM/utils'
10
+
11
+ export type { EVMTransactionRequest, EVMUnsignedTransaction } from './EVM/types'
12
+
13
+ // Bitcoin
14
+ export { Bitcoin } from './Bitcoin/Bitcoin'
15
+
16
+ export { BTCRpcAdapters, BTCRpcAdapter } from './Bitcoin/BTCRpcAdapter'
17
+
18
+ export type {
19
+ BTCTransactionRequest,
20
+ BTCUnsignedTransaction,
21
+ BTCTransaction,
22
+ BTCOutput,
23
+ BTCInput,
24
+ BTCNetworkIds,
25
+ } from './Bitcoin/types'
26
+
27
+ // Cosmos
28
+ export { Cosmos } from './Cosmos/Cosmos'
29
+
30
+ export type {
31
+ CosmosNetworkIds,
32
+ CosmosTransactionRequest,
33
+ CosmosUnsignedTransaction,
34
+ } from './Cosmos/types'
@@ -0,0 +1,35 @@
1
+ import { type SignArgs } from '@chains/ChainSignatureContract'
2
+
3
+ interface SuccessResponse {
4
+ transactionHash: string
5
+ success: true
6
+ }
7
+
8
+ interface FailureResponse {
9
+ success: false
10
+ errorMessage: string
11
+ }
12
+
13
+ export type Response = SuccessResponse | FailureResponse
14
+
15
+ export type MPCPayloads = Array<{ index: number; payload: SignArgs['payload'] }>
16
+
17
+ export type UncompressedPubKeySEC1 = `04${string}`
18
+
19
+ export type KeyDerivationPath = string
20
+
21
+ export interface RSVSignature {
22
+ r: string
23
+ s: string
24
+ v: number
25
+ }
26
+
27
+ export interface MPCSignature {
28
+ big_r: {
29
+ affine_point: string
30
+ }
31
+ s: {
32
+ scalar: string
33
+ }
34
+ recovery_id: number
35
+ }
@@ -0,0 +1,40 @@
1
+ import {
2
+ type MPCSignature,
3
+ type RSVSignature,
4
+ type UncompressedPubKeySEC1,
5
+ } from '@chains/types'
6
+
7
+ export const toRSV = (signature: MPCSignature): RSVSignature => {
8
+ return {
9
+ r: signature.big_r.affine_point.substring(2),
10
+ s: signature.s.scalar,
11
+ v: signature.recovery_id,
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Compresses an uncompressed public key to its compressed format following SEC1 standards.
17
+ * In SEC1, a compressed public key consists of a prefix (02 or 03) followed by the x-coordinate.
18
+ * The prefix indicates whether the y-coordinate is even (02) or odd (03).
19
+ *
20
+ * @param uncompressedPubKeySEC1 - The uncompressed public key in hex format, with or without '04' prefix
21
+ * @returns The compressed public key in hex format
22
+ * @throws Error if the uncompressed public key length is invalid
23
+ */
24
+ export const compressPubKey = (
25
+ uncompressedPubKeySEC1: UncompressedPubKeySEC1
26
+ ): string => {
27
+ const slicedPubKey = uncompressedPubKeySEC1.slice(2)
28
+
29
+ if (slicedPubKey.length !== 128) {
30
+ throw new Error('Invalid uncompressed public key length')
31
+ }
32
+
33
+ const x = slicedPubKey.slice(0, 64)
34
+ const y = slicedPubKey.slice(64)
35
+
36
+ const isEven = parseInt(y.slice(-1), 16) % 2 === 0
37
+ const prefix = isEven ? '02' : '03'
38
+
39
+ return prefix + x
40
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * as utils from './utils'
2
+ export * from './chains'
@@ -0,0 +1 @@
1
+ export * as near from './near'