@sig-net/signet.js 0.5.0-rc1
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/LICENSE +19 -0
- package/README.md +95 -0
- package/dist/index.cjs +3223 -0
- package/dist/index.d.cts +2098 -0
- package/dist/index.d.ts +2096 -0
- package/dist/index.js +3167 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/package.json +109 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3167 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { secp256k1 } from "@noble/curves/secp256k1.js";
|
|
3
|
+
import { base58, hex } from "@scure/base";
|
|
4
|
+
import { bytesToHex, concatHex, createPublicClient, encodeAbiParameters, encodeFunctionData, encodePacked, getAddress, hashMessage, hashTypedData, http, keccak256, numberToHex, parseAbiParameters, parseTransaction, recoverAddress, serializeTransaction, toBytes, withRetry } from "viem";
|
|
5
|
+
import * as bitcoin from "bitcoinjs-lib";
|
|
6
|
+
import coinselect from "coinselect";
|
|
7
|
+
import { encodeSecp256k1Pubkey } from "@cosmjs/amino";
|
|
8
|
+
import { ripemd160, sha256 } from "@cosmjs/crypto";
|
|
9
|
+
import { fromBase64, fromHex, toBase64, toBech32, toHex } from "@cosmjs/encoding";
|
|
10
|
+
import { Registry, encodePubkey, makeAuthInfoBytes, makeSignBytes, makeSignDoc } from "@cosmjs/proto-signing";
|
|
11
|
+
import { GasPrice, StargateClient, calculateFee } from "@cosmjs/stargate";
|
|
12
|
+
import { SignMode } from "cosmjs-types/cosmos/tx/signing/v1beta1/signing";
|
|
13
|
+
import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx";
|
|
14
|
+
import { assetLists, chains } from "chain-registry";
|
|
15
|
+
import "viem/chains";
|
|
16
|
+
import * as anchor from "@coral-xyz/anchor";
|
|
17
|
+
import { EventParser, Program } from "@coral-xyz/anchor";
|
|
18
|
+
import { Connection, PublicKey, Transaction, TransactionExpiredTimeoutError } from "@solana/web3.js";
|
|
19
|
+
//#region src/constants.ts
|
|
20
|
+
var constants_exports = /* @__PURE__ */ __exportAll({
|
|
21
|
+
CHAINS: () => CHAINS,
|
|
22
|
+
CONTRACT_ADDRESSES: () => CONTRACT_ADDRESSES,
|
|
23
|
+
ENVS: () => ENVS,
|
|
24
|
+
KDF_CHAIN_IDS: () => KDF_CHAIN_IDS,
|
|
25
|
+
ROOT_PUBLIC_KEYS: () => ROOT_PUBLIC_KEYS
|
|
26
|
+
});
|
|
27
|
+
const ENVS = {
|
|
28
|
+
TESTNET_DEV: "TESTNET_DEV",
|
|
29
|
+
TESTNET: "TESTNET",
|
|
30
|
+
MAINNET: "MAINNET"
|
|
31
|
+
};
|
|
32
|
+
const CHAINS = {
|
|
33
|
+
ETHEREUM: "ETHEREUM",
|
|
34
|
+
SOLANA: "SOLANA",
|
|
35
|
+
CANTON: "CANTON"
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Root public keys for the Sig Network Smart Contracts across different environments.
|
|
39
|
+
*
|
|
40
|
+
* These keys should never change.
|
|
41
|
+
*/
|
|
42
|
+
const ROOT_PUBLIC_KEYS = {
|
|
43
|
+
[ENVS.TESTNET_DEV]: "secp256k1:54hU5wcCmVUPFWLDALXMh1fFToZsVXrx9BbTbHzSfQq1Kd1rJZi52iPa4QQxo6s5TgjWqgpY8HamYuUDzG6fAaUq",
|
|
44
|
+
[ENVS.TESTNET]: "secp256k1:3Ww8iFjqTHufye5aRGUvrQqETegR4gVUcW8FX5xzscaN9ENhpkffojsxJwi6N1RbbHMTxYa9UyKeqK3fsMuwxjR5",
|
|
45
|
+
[ENVS.MAINNET]: "secp256k1:4tY4qMzusmgX5wYdG35663Y3Qar3CTbpApotwk9ZKLoF79XA4DjG8XoByaKdNHKQX9Lz5hd7iJqsWdTKyA7dKa6Z"
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Chain IDs used in the key derivation function (KDF) for deriving child public keys to
|
|
49
|
+
* distinguish between different chains.
|
|
50
|
+
*
|
|
51
|
+
* @see {@link deriveChildPublicKey} in cryptography.ts for usage details
|
|
52
|
+
*/
|
|
53
|
+
const KDF_CHAIN_IDS = {
|
|
54
|
+
[CHAINS.ETHEREUM]: "eip155:1",
|
|
55
|
+
[CHAINS.SOLANA]: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
|
56
|
+
[CHAINS.CANTON]: "canton:global"
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Contract addresses for different chains and environments.
|
|
60
|
+
*
|
|
61
|
+
* - Testnet Dev: Used for internal development, very unstable
|
|
62
|
+
* - Testnet: Used for external development, stable
|
|
63
|
+
* - Mainnet: Production contract address
|
|
64
|
+
*
|
|
65
|
+
* @see ChainSignatureContract documentation for implementation details
|
|
66
|
+
*/
|
|
67
|
+
const CONTRACT_ADDRESSES = {
|
|
68
|
+
[CHAINS.ETHEREUM]: {
|
|
69
|
+
[ENVS.TESTNET_DEV]: "0x69C6b28Fdc74618817fa380De29a653060e14009",
|
|
70
|
+
[ENVS.TESTNET]: "0x83458E8Bf8206131Fe5c05127007FA164c0948A2",
|
|
71
|
+
[ENVS.MAINNET]: "0xf8bdC0612361a1E49a8E01423d4C0cFc5dF4791A"
|
|
72
|
+
},
|
|
73
|
+
[CHAINS.SOLANA]: {
|
|
74
|
+
[ENVS.TESTNET_DEV]: "SigDuEPNeDjh3oJv7MUraPN7zaTFomS6ZWfpXwjUg4B",
|
|
75
|
+
[ENVS.TESTNET]: "SigTVbfRK9LsXWpSv9KgpabrQcFKr5hDdUwMhYsXyKg",
|
|
76
|
+
[ENVS.MAINNET]: "SigMcRMjKfnC7RDG5q4yUMZM1s5KJ9oYTPP4NmJRDRw"
|
|
77
|
+
},
|
|
78
|
+
[CHAINS.CANTON]: {
|
|
79
|
+
[ENVS.TESTNET_DEV]: "",
|
|
80
|
+
[ENVS.TESTNET]: "",
|
|
81
|
+
[ENVS.MAINNET]: ""
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/utils/cryptography.ts
|
|
86
|
+
var cryptography_exports = /* @__PURE__ */ __exportAll({
|
|
87
|
+
compressPubKey: () => compressPubKey,
|
|
88
|
+
deriveChildPublicKey: () => deriveChildPublicKey,
|
|
89
|
+
normalizeToUncompressedPubKey: () => normalizeToUncompressedPubKey,
|
|
90
|
+
toRSV: () => toRSV,
|
|
91
|
+
verifyRecoveredAddress: () => verifyRecoveredAddress
|
|
92
|
+
});
|
|
93
|
+
const toRSV = (signature) => {
|
|
94
|
+
if ("bigR" in signature && "x" in signature.bigR && "s" in signature && typeof signature.s === "bigint") return {
|
|
95
|
+
r: signature.bigR.x.toString(16).padStart(64, "0"),
|
|
96
|
+
s: signature.s.toString(16).padStart(64, "0"),
|
|
97
|
+
v: signature.recoveryId + 27
|
|
98
|
+
};
|
|
99
|
+
throw new Error("Invalid signature format");
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Compresses an uncompressed public key to its compressed format following SEC1 standards.
|
|
103
|
+
* In SEC1, a compressed public key consists of a prefix (02 or 03) followed by the x-coordinate.
|
|
104
|
+
* The prefix indicates whether the y-coordinate is even (02) or odd (03).
|
|
105
|
+
*
|
|
106
|
+
* @param uncompressedPubKeySEC1 - The uncompressed public key in hex format, with or without '04' prefix
|
|
107
|
+
* @returns The compressed public key in hex format
|
|
108
|
+
* @throws Error if the uncompressed public key length is invalid
|
|
109
|
+
*/
|
|
110
|
+
const compressPubKey = (uncompressedPubKeySEC1) => {
|
|
111
|
+
const slicedPubKey = uncompressedPubKeySEC1.slice(2);
|
|
112
|
+
if (slicedPubKey.length !== 128) throw new Error("Invalid uncompressed public key length");
|
|
113
|
+
const x = slicedPubKey.slice(0, 64);
|
|
114
|
+
const y = slicedPubKey.slice(64);
|
|
115
|
+
return (parseInt(y.slice(-1), 16) % 2 === 0 ? "02" : "03") + x;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Normalizes any supported public key format to an uncompressed SEC1 public key.
|
|
119
|
+
*
|
|
120
|
+
* Supported formats:
|
|
121
|
+
* - NAJ format: `secp256k1:base58...`
|
|
122
|
+
* - Uncompressed SEC1: `04` prefix + 128 hex chars (64 bytes)
|
|
123
|
+
* - Compressed SEC1: `02` or `03` prefix + 64 hex chars (32 bytes)
|
|
124
|
+
*/
|
|
125
|
+
const normalizeToUncompressedPubKey = (key) => {
|
|
126
|
+
if (key.startsWith("secp256k1:")) {
|
|
127
|
+
const decodedKey = base58.decode(key.split(":")[1]);
|
|
128
|
+
return `04${hex.encode(decodedKey)}`;
|
|
129
|
+
}
|
|
130
|
+
if (key.startsWith("04") && key.length === 130) return key;
|
|
131
|
+
if ((key.startsWith("02") || key.startsWith("03")) && key.length === 66) return secp256k1.Point.fromHex(key).toHex(false);
|
|
132
|
+
throw new Error(`Unsupported public key format. Expected NAJ (secp256k1:base58...), uncompressed (04 + 128 hex), or compressed (02/03 + 64 hex) key. Received: ${key.slice(0, 20)}...`);
|
|
133
|
+
};
|
|
134
|
+
const EPSILON_DERIVATION_PREFIX = "sig.network v2.0.0 epsilon derivation";
|
|
135
|
+
/**
|
|
136
|
+
* Derives a child public key from a parent public key using the Sig.Network epsilon derivation scheme.
|
|
137
|
+
* The parent public keys are defined in @constants.ts
|
|
138
|
+
*
|
|
139
|
+
* @param rootUncompressedPubKeySEC1 - The parent public key in uncompressed SEC1 format (e.g. 04 || x || y)
|
|
140
|
+
* @param predecessorId - The predecessor ID is the address of the account calling the signer contract (e.g EOA or Contract Address)
|
|
141
|
+
* @param path - Optional derivation path suffix (defaults to empty string)
|
|
142
|
+
* @param chainId - CAIP-2 chain identifier used for derivation
|
|
143
|
+
* @param keyVersion - Key version (must be >= 1 for v2 derivation)
|
|
144
|
+
* @returns The derived child public key in uncompressed SEC1 format (04 || x || y)
|
|
145
|
+
*/
|
|
146
|
+
function deriveChildPublicKey(rootUncompressedPubKeySEC1, predecessorId, path = "", chainId, keyVersion) {
|
|
147
|
+
if (!Object.values(KDF_CHAIN_IDS).includes(chainId)) throw new Error("Invalid chain ID");
|
|
148
|
+
if (keyVersion < 1) throw new Error("key_version 0 (legacy v1 derivation) is not supported. Use key_version >= 1.");
|
|
149
|
+
const derivationPath = `${EPSILON_DERIVATION_PREFIX}:${chainId}:${predecessorId}:${path}`;
|
|
150
|
+
const scalarHex = keccak256(new TextEncoder().encode(derivationPath)).slice(2);
|
|
151
|
+
const oldPoint = secp256k1.Point.fromHex(rootUncompressedPubKeySEC1);
|
|
152
|
+
const scalarTimesG = secp256k1.Point.BASE.multiply(BigInt(`0x${scalarHex}`));
|
|
153
|
+
return oldPoint.add(scalarTimesG).toHex(false);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Verifies that a secp256k1 signature was created by the expected derived address
|
|
157
|
+
* by recovering the signing address and comparing it with the address derived from the contract.
|
|
158
|
+
*
|
|
159
|
+
* @param signature - The RSV signature to verify
|
|
160
|
+
* @param payload - The original message that was signed (as byte array)
|
|
161
|
+
* @param requesterAddress - The address of the requester
|
|
162
|
+
* @param path - The derivation path used for key generation
|
|
163
|
+
* @param contract - The contract instance for deriving addresses
|
|
164
|
+
* @returns Promise resolving to true if the recovered address matches the expected address
|
|
165
|
+
*/
|
|
166
|
+
async function verifyRecoveredAddress(signature, payload, requesterAddress, path, contract, keyVersion) {
|
|
167
|
+
try {
|
|
168
|
+
const { address: expectedAddress } = await new EVM({
|
|
169
|
+
publicClient: createPublicClient({ transport: http("https://dontcare.com") }),
|
|
170
|
+
contract
|
|
171
|
+
}).deriveAddressAndPublicKey(requesterAddress, path, keyVersion);
|
|
172
|
+
return (await recoverAddress({
|
|
173
|
+
hash: new Uint8Array(payload),
|
|
174
|
+
signature: {
|
|
175
|
+
r: `0x${signature.r}`,
|
|
176
|
+
s: `0x${signature.s}`,
|
|
177
|
+
yParity: signature.v - 27
|
|
178
|
+
}
|
|
179
|
+
})).toLowerCase() === expectedAddress.toLowerCase();
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error("Signature verification failed:", error);
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region src/utils/index.ts
|
|
187
|
+
var utils_exports = /* @__PURE__ */ __exportAll({ cryptography: () => cryptography_exports });
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/chain-adapters/ChainAdapter.ts
|
|
190
|
+
var ChainAdapter = class {};
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region src/chain-adapters/EVM/utils.ts
|
|
193
|
+
async function fetchEVMFeeProperties(client, transaction) {
|
|
194
|
+
const [gas, feeData] = await Promise.all([client.estimateGas(transaction), client.estimateFeesPerGas()]);
|
|
195
|
+
return {
|
|
196
|
+
gas,
|
|
197
|
+
maxFeePerGas: feeData.maxFeePerGas ?? BigInt(1e10),
|
|
198
|
+
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? BigInt(1e10)
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/chain-adapters/EVM/EVM.ts
|
|
203
|
+
/**
|
|
204
|
+
* Implementation of the ChainAdapter interface for EVM-compatible networks.
|
|
205
|
+
* Handles interactions with Ethereum Virtual Machine based blockchains like Ethereum, BSC, Polygon, etc.
|
|
206
|
+
*/
|
|
207
|
+
var EVM = class extends ChainAdapter {
|
|
208
|
+
client;
|
|
209
|
+
contract;
|
|
210
|
+
/**
|
|
211
|
+
* Creates a new EVM chain instance
|
|
212
|
+
* @param params - Configuration parameters
|
|
213
|
+
* @param params.publicClient - A Viem PublicClient instance for reading from the blockchain
|
|
214
|
+
* @param params.contract - Instance of the chain signature contract for MPC operations
|
|
215
|
+
*/
|
|
216
|
+
constructor({ publicClient, contract }) {
|
|
217
|
+
super();
|
|
218
|
+
this.contract = contract;
|
|
219
|
+
this.client = publicClient;
|
|
220
|
+
}
|
|
221
|
+
async attachGasAndNonce(transaction) {
|
|
222
|
+
const fees = await fetchEVMFeeProperties(this.client, transaction);
|
|
223
|
+
const nonce = await this.client.getTransactionCount({ address: transaction.from });
|
|
224
|
+
const { from: _from, ...rest } = transaction;
|
|
225
|
+
return {
|
|
226
|
+
...fees,
|
|
227
|
+
nonce,
|
|
228
|
+
chainId: Number(await this.client.getChainId()),
|
|
229
|
+
type: "eip1559",
|
|
230
|
+
...rest
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
transformRSVSignature(signature) {
|
|
234
|
+
return {
|
|
235
|
+
r: `0x${signature.r}`,
|
|
236
|
+
s: `0x${signature.s}`,
|
|
237
|
+
yParity: signature.v - 27
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
assembleSignature(signature) {
|
|
241
|
+
const { r, s, yParity } = this.transformRSVSignature(signature);
|
|
242
|
+
if (yParity === void 0) throw new Error("Missing yParity");
|
|
243
|
+
return concatHex([
|
|
244
|
+
r,
|
|
245
|
+
s,
|
|
246
|
+
numberToHex(yParity + 27, { size: 1 })
|
|
247
|
+
]);
|
|
248
|
+
}
|
|
249
|
+
async deriveAddressAndPublicKey(predecessor, path, keyVersion) {
|
|
250
|
+
const uncompressedPubKey = await this.contract.getDerivedPublicKey({
|
|
251
|
+
path,
|
|
252
|
+
predecessor,
|
|
253
|
+
keyVersion
|
|
254
|
+
});
|
|
255
|
+
if (!uncompressedPubKey) throw new Error("Failed to get derived public key");
|
|
256
|
+
return {
|
|
257
|
+
address: getAddress(`0x${keccak256(`0x${uncompressedPubKey.startsWith("04") ? uncompressedPubKey.slice(2) : uncompressedPubKey}`).slice(-40)}`),
|
|
258
|
+
publicKey: uncompressedPubKey
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
async getBalance(address) {
|
|
262
|
+
return {
|
|
263
|
+
balance: await this.client.getBalance({ address }),
|
|
264
|
+
decimals: 18
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
serializeTransaction(transaction) {
|
|
268
|
+
return serializeTransaction(transaction);
|
|
269
|
+
}
|
|
270
|
+
deserializeTransaction(serialized) {
|
|
271
|
+
return parseTransaction(serialized);
|
|
272
|
+
}
|
|
273
|
+
async prepareTransactionForSigning(transactionRequest) {
|
|
274
|
+
const transaction = await this.attachGasAndNonce(transactionRequest);
|
|
275
|
+
const txHash = toBytes(keccak256(serializeTransaction(transaction)));
|
|
276
|
+
return {
|
|
277
|
+
transaction,
|
|
278
|
+
hashesToSign: [Array.from(txHash)]
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
async prepareMessageForSigning(message) {
|
|
282
|
+
return { hashToSign: Array.from(toBytes(hashMessage(message))) };
|
|
283
|
+
}
|
|
284
|
+
async prepareTypedDataForSigning(typedDataRequest) {
|
|
285
|
+
return { hashToSign: Array.from(toBytes(hashTypedData(typedDataRequest))) };
|
|
286
|
+
}
|
|
287
|
+
finalizeTransactionSigning({ transaction, rsvSignatures }) {
|
|
288
|
+
return serializeTransaction(transaction, this.transformRSVSignature(rsvSignatures[0]));
|
|
289
|
+
}
|
|
290
|
+
finalizeMessageSigning({ rsvSignature }) {
|
|
291
|
+
return this.assembleSignature(rsvSignature);
|
|
292
|
+
}
|
|
293
|
+
finalizeTypedDataSigning({ rsvSignature }) {
|
|
294
|
+
return this.assembleSignature(rsvSignature);
|
|
295
|
+
}
|
|
296
|
+
async broadcastTx(txSerialized) {
|
|
297
|
+
try {
|
|
298
|
+
return await this.client.sendRawTransaction({ serializedTransaction: txSerialized });
|
|
299
|
+
} catch (error) {
|
|
300
|
+
console.error("Transaction broadcast failed:", error);
|
|
301
|
+
throw new Error("Failed to broadcast transaction.", { cause: error });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/chain-adapters/EVM/index.ts
|
|
307
|
+
var EVM_exports = /* @__PURE__ */ __exportAll({
|
|
308
|
+
EVM: () => EVM,
|
|
309
|
+
fetchEVMFeeProperties: () => fetchEVMFeeProperties
|
|
310
|
+
});
|
|
311
|
+
//#endregion
|
|
312
|
+
//#region src/chain-adapters/Bitcoin/utils.ts
|
|
313
|
+
function parseBTCNetwork(network) {
|
|
314
|
+
switch (network.toLowerCase()) {
|
|
315
|
+
case "mainnet": return bitcoin.networks.bitcoin;
|
|
316
|
+
case "testnet": return bitcoin.networks.testnet;
|
|
317
|
+
case "regtest": return bitcoin.networks.regtest;
|
|
318
|
+
default: throw new Error(`Unknown Bitcoin network: ${network}`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
//#endregion
|
|
322
|
+
//#region src/chain-adapters/Bitcoin/Bitcoin.ts
|
|
323
|
+
/**
|
|
324
|
+
* Implementation of the ChainAdapter interface for Bitcoin network.
|
|
325
|
+
* Handles interactions with both Bitcoin mainnet and testnet, supporting P2WPKH transactions.
|
|
326
|
+
*/
|
|
327
|
+
var Bitcoin = class Bitcoin extends ChainAdapter {
|
|
328
|
+
static SATOSHIS_PER_BTC = 1e8;
|
|
329
|
+
network;
|
|
330
|
+
btcRpcAdapter;
|
|
331
|
+
contract;
|
|
332
|
+
/**
|
|
333
|
+
* Creates a new Bitcoin chain instance
|
|
334
|
+
* @param params - Configuration parameters
|
|
335
|
+
* @param params.network - Network identifier (mainnet/testnet)
|
|
336
|
+
* @param params.contract - Instance of the chain signature contract for MPC operations
|
|
337
|
+
* @param params.btcRpcAdapter - Bitcoin RPC adapter for network interactions
|
|
338
|
+
*/
|
|
339
|
+
constructor({ network, contract, btcRpcAdapter }) {
|
|
340
|
+
super();
|
|
341
|
+
this.network = network;
|
|
342
|
+
this.btcRpcAdapter = btcRpcAdapter;
|
|
343
|
+
this.contract = contract;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Converts satoshis to BTC
|
|
347
|
+
* @param satoshis - Amount in satoshis
|
|
348
|
+
* @returns Amount in BTC
|
|
349
|
+
*/
|
|
350
|
+
static toBTC(satoshis) {
|
|
351
|
+
return satoshis / Bitcoin.SATOSHIS_PER_BTC;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Converts BTC to satoshis
|
|
355
|
+
* @param btc - Amount in BTC
|
|
356
|
+
* @returns Amount in satoshis (rounded)
|
|
357
|
+
*/
|
|
358
|
+
static toSatoshi(btc) {
|
|
359
|
+
return Math.round(btc * Bitcoin.SATOSHIS_PER_BTC);
|
|
360
|
+
}
|
|
361
|
+
async fetchTransaction(transactionId) {
|
|
362
|
+
const data = await this.btcRpcAdapter.getTransaction(transactionId);
|
|
363
|
+
const tx = new bitcoin.Transaction();
|
|
364
|
+
data.vout.forEach((vout) => {
|
|
365
|
+
const scriptPubKey = Buffer.from(vout.scriptpubkey, "hex");
|
|
366
|
+
tx.addOutput(scriptPubKey, BigInt(vout.value));
|
|
367
|
+
});
|
|
368
|
+
return tx;
|
|
369
|
+
}
|
|
370
|
+
static transformRSVSignature(signature) {
|
|
371
|
+
const r = signature.r.padStart(64, "0");
|
|
372
|
+
const s = signature.s.padStart(64, "0");
|
|
373
|
+
const rawSignature = Buffer.from(r + s, "hex");
|
|
374
|
+
if (rawSignature.length !== 64) throw new Error("Invalid signature length.");
|
|
375
|
+
return rawSignature;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Creates a Partially Signed Bitcoin Transaction (PSBT)
|
|
379
|
+
* @param params - Parameters for creating the PSBT
|
|
380
|
+
* @param params.transactionRequest - Transaction request containing inputs and outputs
|
|
381
|
+
* @returns Created PSBT instance
|
|
382
|
+
*/
|
|
383
|
+
async createPSBT({ transactionRequest }) {
|
|
384
|
+
const { inputs, outputs } = transactionRequest.inputs && transactionRequest.outputs ? transactionRequest : await this.btcRpcAdapter.selectUTXOs(transactionRequest.from, [{
|
|
385
|
+
address: transactionRequest.to,
|
|
386
|
+
value: parseFloat(transactionRequest.value)
|
|
387
|
+
}]);
|
|
388
|
+
const psbt = new bitcoin.Psbt({ network: parseBTCNetwork(this.network) });
|
|
389
|
+
await Promise.all(inputs.map(async (input) => {
|
|
390
|
+
if (!input.scriptPubKey) input.scriptPubKey = (await this.fetchTransaction(input.txid)).outs[input.vout].script;
|
|
391
|
+
psbt.addInput({
|
|
392
|
+
hash: input.txid,
|
|
393
|
+
index: input.vout,
|
|
394
|
+
witnessUtxo: {
|
|
395
|
+
script: input.scriptPubKey,
|
|
396
|
+
value: BigInt(input.value)
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}));
|
|
400
|
+
outputs.forEach((out) => {
|
|
401
|
+
if ("address" in out) psbt.addOutput({
|
|
402
|
+
address: out.address,
|
|
403
|
+
value: BigInt(out.value)
|
|
404
|
+
});
|
|
405
|
+
else if ("script" in out) psbt.addOutput({
|
|
406
|
+
script: out.script,
|
|
407
|
+
value: BigInt(out.value)
|
|
408
|
+
});
|
|
409
|
+
else if (transactionRequest.from !== void 0) psbt.addOutput({
|
|
410
|
+
value: BigInt(out.value),
|
|
411
|
+
address: transactionRequest.from
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
return psbt;
|
|
415
|
+
}
|
|
416
|
+
async getBalance(address) {
|
|
417
|
+
return {
|
|
418
|
+
balance: BigInt(await this.btcRpcAdapter.getBalance(address)),
|
|
419
|
+
decimals: 8
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
async deriveAddressAndPublicKey(predecessor, path, keyVersion) {
|
|
423
|
+
const uncompressedPubKey = await this.contract.getDerivedPublicKey({
|
|
424
|
+
path,
|
|
425
|
+
predecessor,
|
|
426
|
+
keyVersion
|
|
427
|
+
});
|
|
428
|
+
if (!uncompressedPubKey) throw new Error("Failed to get derived public key");
|
|
429
|
+
const derivedKey = compressPubKey(uncompressedPubKey);
|
|
430
|
+
const publicKeyBuffer = Buffer.from(derivedKey, "hex");
|
|
431
|
+
const network = parseBTCNetwork(this.network);
|
|
432
|
+
const { address } = bitcoin.payments.p2wpkh({
|
|
433
|
+
pubkey: publicKeyBuffer,
|
|
434
|
+
network
|
|
435
|
+
});
|
|
436
|
+
if (!address) throw new Error("Failed to generate Bitcoin address");
|
|
437
|
+
return {
|
|
438
|
+
address,
|
|
439
|
+
publicKey: derivedKey
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
serializeTransaction(transaction) {
|
|
443
|
+
return JSON.stringify({
|
|
444
|
+
psbt: transaction.psbt.toHex(),
|
|
445
|
+
publicKey: transaction.publicKey
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
deserializeTransaction(serialized) {
|
|
449
|
+
const transactionJSON = JSON.parse(serialized);
|
|
450
|
+
return {
|
|
451
|
+
psbt: bitcoin.Psbt.fromHex(transactionJSON.psbt),
|
|
452
|
+
publicKey: transactionJSON.publicKey
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
async prepareTransactionForSigning(transactionRequest) {
|
|
456
|
+
const publicKeyBuffer = Buffer.from(transactionRequest.publicKey, "hex");
|
|
457
|
+
const psbt = await this.createPSBT({ transactionRequest });
|
|
458
|
+
const psbtHex = psbt.toHex();
|
|
459
|
+
const hashesToSign = [];
|
|
460
|
+
const mockKeyPair = (index) => ({
|
|
461
|
+
publicKey: publicKeyBuffer,
|
|
462
|
+
sign: (hash) => {
|
|
463
|
+
hashesToSign[index] = Array.from(hash);
|
|
464
|
+
return /* @__PURE__ */ new Uint8Array(64);
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
for (let index = 0; index < psbt.inputCount; index++) psbt.signInput(index, mockKeyPair(index));
|
|
468
|
+
return {
|
|
469
|
+
transaction: {
|
|
470
|
+
psbt: bitcoin.Psbt.fromHex(psbtHex),
|
|
471
|
+
publicKey: transactionRequest.publicKey
|
|
472
|
+
},
|
|
473
|
+
hashesToSign
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
finalizeTransactionSigning({ transaction: { psbt, publicKey }, rsvSignatures }) {
|
|
477
|
+
const publicKeyBuffer = Buffer.from(publicKey, "hex");
|
|
478
|
+
const keyPair = (index) => ({
|
|
479
|
+
publicKey: publicKeyBuffer,
|
|
480
|
+
sign: () => {
|
|
481
|
+
const mpcSignature = rsvSignatures[index];
|
|
482
|
+
return Bitcoin.transformRSVSignature(mpcSignature);
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
for (let index = 0; index < psbt.inputCount; index++) psbt.signInput(index, keyPair(index));
|
|
486
|
+
psbt.finalizeAllInputs();
|
|
487
|
+
return psbt.extractTransaction().toHex();
|
|
488
|
+
}
|
|
489
|
+
async broadcastTx(txSerialized) {
|
|
490
|
+
return await this.btcRpcAdapter.broadcastTransaction(txSerialized);
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
//#endregion
|
|
494
|
+
//#region src/chain-adapters/Bitcoin/BTCRpcAdapter/BTCRpcAdapter.ts
|
|
495
|
+
var BTCRpcAdapter = class {};
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region src/chain-adapters/Bitcoin/BTCRpcAdapter/Mempool/Mempool.ts
|
|
498
|
+
var Mempool = class extends BTCRpcAdapter {
|
|
499
|
+
providerUrl;
|
|
500
|
+
constructor(providerUrl) {
|
|
501
|
+
super();
|
|
502
|
+
this.providerUrl = providerUrl;
|
|
503
|
+
}
|
|
504
|
+
async fetchFeeRate(confirmationTarget = 6) {
|
|
505
|
+
const data = await (await fetch(`${this.providerUrl}/v1/fees/recommended`)).json();
|
|
506
|
+
if (confirmationTarget <= 1) return data.fastestFee;
|
|
507
|
+
else if (confirmationTarget <= 3) return data.halfHourFee;
|
|
508
|
+
else if (confirmationTarget <= 6) return data.hourFee;
|
|
509
|
+
else return data.economyFee;
|
|
510
|
+
}
|
|
511
|
+
async fetchUTXOs(address) {
|
|
512
|
+
try {
|
|
513
|
+
return await (await fetch(`${this.providerUrl}/address/${address}/utxo`)).json();
|
|
514
|
+
} catch (error) {
|
|
515
|
+
console.error("Failed to fetch UTXOs:", error);
|
|
516
|
+
return [];
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
async selectUTXOs(from, targets, confirmationTarget = 6) {
|
|
520
|
+
const utxos = await this.fetchUTXOs(from);
|
|
521
|
+
const feeRate = await this.fetchFeeRate(confirmationTarget);
|
|
522
|
+
const ret = coinselect(utxos, targets, Math.ceil(feeRate + 1));
|
|
523
|
+
if (!ret.inputs || !ret.outputs) throw new Error("Invalid transaction: coinselect failed to find a suitable set of inputs and outputs. This could be due to insufficient funds, or no inputs being available that meet the criteria.");
|
|
524
|
+
return {
|
|
525
|
+
inputs: ret.inputs,
|
|
526
|
+
outputs: ret.outputs
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
async broadcastTransaction(transactionHex) {
|
|
530
|
+
const response = await fetch(`${this.providerUrl}/tx`, {
|
|
531
|
+
method: "POST",
|
|
532
|
+
body: transactionHex
|
|
533
|
+
});
|
|
534
|
+
if (response.ok) return await response.text();
|
|
535
|
+
throw new Error(`Failed to broadcast transaction: ${await response.text()}`);
|
|
536
|
+
}
|
|
537
|
+
async getBalance(address) {
|
|
538
|
+
const data = await (await fetch(`${this.providerUrl}/address/${address}`)).json();
|
|
539
|
+
return data.chain_stats.funded_txo_sum - data.chain_stats.spent_txo_sum;
|
|
540
|
+
}
|
|
541
|
+
async getTransaction(txid) {
|
|
542
|
+
return await (await fetch(`${this.providerUrl}/tx/${txid}`)).json();
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
//#endregion
|
|
546
|
+
//#region src/chain-adapters/Bitcoin/BTCRpcAdapter/index.ts
|
|
547
|
+
const BTCRpcAdapters = { Mempool };
|
|
548
|
+
//#endregion
|
|
549
|
+
//#region src/chain-adapters/Bitcoin/index.ts
|
|
550
|
+
var Bitcoin_exports = /* @__PURE__ */ __exportAll({
|
|
551
|
+
BTCRpcAdapter: () => BTCRpcAdapter,
|
|
552
|
+
BTCRpcAdapters: () => BTCRpcAdapters,
|
|
553
|
+
Bitcoin: () => Bitcoin
|
|
554
|
+
});
|
|
555
|
+
//#endregion
|
|
556
|
+
//#region src/chain-adapters/Cosmos/utils.ts
|
|
557
|
+
const fetchChainInfo = async (chainId) => {
|
|
558
|
+
const chainInfo = chains.find((chain) => chain.chainId === chainId);
|
|
559
|
+
if (!chainInfo) throw new Error(`Chain info not found for chainId: ${chainId}`);
|
|
560
|
+
const { bech32Prefix: prefix, chainId: expectedChainId } = chainInfo;
|
|
561
|
+
const denom = chainInfo.staking?.stakingTokens?.[0]?.denom;
|
|
562
|
+
const rpcUrl = chainInfo.apis?.rpc?.[0]?.address;
|
|
563
|
+
const restUrl = chainInfo.apis?.rest?.[0]?.address;
|
|
564
|
+
const gasPrice = chainInfo.fees?.feeTokens?.[0]?.averageGasPrice;
|
|
565
|
+
if (!prefix || !denom || !rpcUrl || !restUrl || !expectedChainId || gasPrice === void 0) throw new Error(`Missing required chain information for ${chainInfo.chainName}`);
|
|
566
|
+
const asset = assetLists.find((al) => al.chainName === chainInfo.chainName)?.assets.find((asset) => asset.base === denom);
|
|
567
|
+
const decimals = asset?.denomUnits.find((unit) => unit.denom === asset.display)?.exponent;
|
|
568
|
+
if (decimals === void 0) throw new Error(`Could not find decimals for ${denom} on chain ${chainInfo.chainName}`);
|
|
569
|
+
return {
|
|
570
|
+
prefix,
|
|
571
|
+
denom,
|
|
572
|
+
rpcUrl,
|
|
573
|
+
restUrl,
|
|
574
|
+
expectedChainId,
|
|
575
|
+
gasPrice,
|
|
576
|
+
decimals
|
|
577
|
+
};
|
|
578
|
+
};
|
|
579
|
+
//#endregion
|
|
580
|
+
//#region src/chain-adapters/Cosmos/Cosmos.ts
|
|
581
|
+
/**
|
|
582
|
+
* Implementation of the ChainAdapter interface for Cosmos-based networks.
|
|
583
|
+
* Handles interactions with Cosmos SDK chains like Cosmos Hub, Osmosis, etc.
|
|
584
|
+
*/
|
|
585
|
+
var Cosmos = class extends ChainAdapter {
|
|
586
|
+
registry;
|
|
587
|
+
chainId;
|
|
588
|
+
contract;
|
|
589
|
+
endpoints;
|
|
590
|
+
/**
|
|
591
|
+
* Creates a new Cosmos chain instance
|
|
592
|
+
* @param params - Configuration parameters
|
|
593
|
+
* @param params.chainId - Chain id for the Cosmos network
|
|
594
|
+
* @param params.contract - Instance of the chain signature contract for MPC operations
|
|
595
|
+
* @param params.endpoints - Optional RPC and REST endpoints
|
|
596
|
+
* @param params.endpoints.rpcUrl - Optional RPC endpoint URL
|
|
597
|
+
* @param params.endpoints.restUrl - Optional REST endpoint URL
|
|
598
|
+
*/
|
|
599
|
+
constructor({ chainId, contract, endpoints }) {
|
|
600
|
+
super();
|
|
601
|
+
this.contract = contract;
|
|
602
|
+
this.registry = new Registry();
|
|
603
|
+
this.chainId = chainId;
|
|
604
|
+
this.endpoints = endpoints;
|
|
605
|
+
}
|
|
606
|
+
transformRSVSignature(rsvSignature) {
|
|
607
|
+
return new Uint8Array([...fromHex(rsvSignature.r), ...fromHex(rsvSignature.s)]);
|
|
608
|
+
}
|
|
609
|
+
async getChainInfo() {
|
|
610
|
+
return {
|
|
611
|
+
...await fetchChainInfo(this.chainId),
|
|
612
|
+
...this.endpoints
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
async getBalance(address) {
|
|
616
|
+
try {
|
|
617
|
+
const { restUrl, denom, decimals } = await this.getChainInfo();
|
|
618
|
+
const response = await fetch(`${restUrl}/cosmos/bank/v1beta1/balances/${address}`);
|
|
619
|
+
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
|
620
|
+
const amount = (await response.json()).balances.find((b) => b.denom === denom)?.amount ?? "0";
|
|
621
|
+
return {
|
|
622
|
+
balance: BigInt(amount),
|
|
623
|
+
decimals
|
|
624
|
+
};
|
|
625
|
+
} catch (error) {
|
|
626
|
+
console.error("Failed to fetch Cosmos balance:", error);
|
|
627
|
+
throw new Error("Failed to fetch Cosmos balance", { cause: error });
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
async deriveAddressAndPublicKey(predecessor, path, keyVersion) {
|
|
631
|
+
const { prefix } = await this.getChainInfo();
|
|
632
|
+
const uncompressedPubKey = await this.contract.getDerivedPublicKey({
|
|
633
|
+
path,
|
|
634
|
+
predecessor,
|
|
635
|
+
keyVersion
|
|
636
|
+
});
|
|
637
|
+
if (!uncompressedPubKey) throw new Error("Failed to get derived public key");
|
|
638
|
+
const derivedKey = compressPubKey(uncompressedPubKey);
|
|
639
|
+
return {
|
|
640
|
+
address: toBech32(prefix, ripemd160(sha256(fromHex(derivedKey)))),
|
|
641
|
+
publicKey: derivedKey
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
serializeTransaction(transaction) {
|
|
645
|
+
return toBase64(TxRaw.encode(transaction).finish());
|
|
646
|
+
}
|
|
647
|
+
deserializeTransaction(serialized) {
|
|
648
|
+
return TxRaw.decode(fromBase64(serialized));
|
|
649
|
+
}
|
|
650
|
+
async prepareTransactionForSigning(transactionRequest) {
|
|
651
|
+
const { denom, rpcUrl, gasPrice } = await this.getChainInfo();
|
|
652
|
+
const publicKeyBytes = fromHex(transactionRequest.publicKey);
|
|
653
|
+
const fee = calculateFee(transactionRequest.gas || 2e5, GasPrice.fromString(`${gasPrice}${denom}`));
|
|
654
|
+
const accountOnChain = await (await StargateClient.connect(rpcUrl)).getAccount(transactionRequest.address);
|
|
655
|
+
if (!accountOnChain) throw new Error(`Account ${transactionRequest.address} does not exist on chain`);
|
|
656
|
+
const { accountNumber, sequence } = accountOnChain;
|
|
657
|
+
const txBodyEncodeObject = {
|
|
658
|
+
typeUrl: "/cosmos.tx.v1beta1.TxBody",
|
|
659
|
+
value: {
|
|
660
|
+
messages: transactionRequest.messages,
|
|
661
|
+
memo: transactionRequest.memo || ""
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
const txBodyBytes = this.registry.encode(txBodyEncodeObject);
|
|
665
|
+
const authInfoBytes = makeAuthInfoBytes([{
|
|
666
|
+
pubkey: encodePubkey(encodeSecp256k1Pubkey(publicKeyBytes)),
|
|
667
|
+
sequence
|
|
668
|
+
}], fee.amount, Number(fee.gas), void 0, void 0, SignMode.SIGN_MODE_DIRECT);
|
|
669
|
+
const signBytes = makeSignBytes(makeSignDoc(txBodyBytes, authInfoBytes, this.chainId, accountNumber));
|
|
670
|
+
const payload = Array.from(sha256(signBytes));
|
|
671
|
+
return {
|
|
672
|
+
transaction: TxRaw.fromPartial({
|
|
673
|
+
bodyBytes: txBodyBytes,
|
|
674
|
+
authInfoBytes,
|
|
675
|
+
signatures: []
|
|
676
|
+
}),
|
|
677
|
+
hashesToSign: [payload]
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
finalizeTransactionSigning({ transaction, rsvSignatures }) {
|
|
681
|
+
transaction.signatures = rsvSignatures.map((sig) => this.transformRSVSignature(sig));
|
|
682
|
+
return toHex(TxRaw.encode(transaction).finish());
|
|
683
|
+
}
|
|
684
|
+
async broadcastTx(txSerialized) {
|
|
685
|
+
try {
|
|
686
|
+
const { rpcUrl } = await this.getChainInfo();
|
|
687
|
+
const client = await StargateClient.connect(rpcUrl);
|
|
688
|
+
const txBytes = fromHex(txSerialized);
|
|
689
|
+
const broadcastResponse = await client.broadcastTx(txBytes);
|
|
690
|
+
if (broadcastResponse.code !== 0) throw new Error(`Broadcast error: ${broadcastResponse.rawLog ?? "unknown error (check events)"}`);
|
|
691
|
+
return broadcastResponse.transactionHash;
|
|
692
|
+
} catch (error) {
|
|
693
|
+
console.error("Transaction broadcast failed:", error);
|
|
694
|
+
throw new Error("Failed to broadcast transaction.", { cause: error });
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
//#endregion
|
|
699
|
+
//#region src/chain-adapters/Cosmos/index.ts
|
|
700
|
+
var Cosmos_exports = /* @__PURE__ */ __exportAll({ Cosmos: () => Cosmos });
|
|
701
|
+
//#endregion
|
|
702
|
+
//#region src/chain-adapters/index.ts
|
|
703
|
+
var chain_adapters_exports = /* @__PURE__ */ __exportAll({
|
|
704
|
+
ChainAdapter: () => ChainAdapter,
|
|
705
|
+
btc: () => Bitcoin_exports,
|
|
706
|
+
cosmos: () => Cosmos_exports,
|
|
707
|
+
evm: () => EVM_exports
|
|
708
|
+
});
|
|
709
|
+
//#endregion
|
|
710
|
+
//#region src/contracts/ChainSignatureContract.ts
|
|
711
|
+
/**
|
|
712
|
+
* Base contract interface required for compatibility with ChainAdapter instances like EVM and Bitcoin.
|
|
713
|
+
*
|
|
714
|
+
* See {@link EVM} and {@link Bitcoin} for example implementations.
|
|
715
|
+
*/
|
|
716
|
+
var BaseChainSignatureContract = class {};
|
|
717
|
+
/**
|
|
718
|
+
* Full contract interface that extends BaseChainSignatureContract to provide all Sig Network Smart Contract capabilities.
|
|
719
|
+
*/
|
|
720
|
+
var ChainSignatureContract$2 = class extends BaseChainSignatureContract {};
|
|
721
|
+
//#endregion
|
|
722
|
+
//#region src/contracts/evm/ChainSignaturesContractABI.ts
|
|
723
|
+
var ChainSignaturesContractABI_exports = /* @__PURE__ */ __exportAll({ abi: () => abi });
|
|
724
|
+
const abi = [
|
|
725
|
+
{
|
|
726
|
+
inputs: [{
|
|
727
|
+
internalType: "address",
|
|
728
|
+
name: "_mpc_network",
|
|
729
|
+
type: "address"
|
|
730
|
+
}, {
|
|
731
|
+
internalType: "uint256",
|
|
732
|
+
name: "_signatureDeposit",
|
|
733
|
+
type: "uint256"
|
|
734
|
+
}],
|
|
735
|
+
stateMutability: "nonpayable",
|
|
736
|
+
type: "constructor"
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
inputs: [],
|
|
740
|
+
name: "AccessControlBadConfirmation",
|
|
741
|
+
type: "error"
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
inputs: [{
|
|
745
|
+
internalType: "address",
|
|
746
|
+
name: "account",
|
|
747
|
+
type: "address"
|
|
748
|
+
}, {
|
|
749
|
+
internalType: "bytes32",
|
|
750
|
+
name: "neededRole",
|
|
751
|
+
type: "bytes32"
|
|
752
|
+
}],
|
|
753
|
+
name: "AccessControlUnauthorizedAccount",
|
|
754
|
+
type: "error"
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
anonymous: false,
|
|
758
|
+
inputs: [
|
|
759
|
+
{
|
|
760
|
+
indexed: true,
|
|
761
|
+
internalType: "bytes32",
|
|
762
|
+
name: "role",
|
|
763
|
+
type: "bytes32"
|
|
764
|
+
},
|
|
765
|
+
{
|
|
766
|
+
indexed: true,
|
|
767
|
+
internalType: "bytes32",
|
|
768
|
+
name: "previousAdminRole",
|
|
769
|
+
type: "bytes32"
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
indexed: true,
|
|
773
|
+
internalType: "bytes32",
|
|
774
|
+
name: "newAdminRole",
|
|
775
|
+
type: "bytes32"
|
|
776
|
+
}
|
|
777
|
+
],
|
|
778
|
+
name: "RoleAdminChanged",
|
|
779
|
+
type: "event"
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
anonymous: false,
|
|
783
|
+
inputs: [
|
|
784
|
+
{
|
|
785
|
+
indexed: true,
|
|
786
|
+
internalType: "bytes32",
|
|
787
|
+
name: "role",
|
|
788
|
+
type: "bytes32"
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
indexed: true,
|
|
792
|
+
internalType: "address",
|
|
793
|
+
name: "account",
|
|
794
|
+
type: "address"
|
|
795
|
+
},
|
|
796
|
+
{
|
|
797
|
+
indexed: true,
|
|
798
|
+
internalType: "address",
|
|
799
|
+
name: "sender",
|
|
800
|
+
type: "address"
|
|
801
|
+
}
|
|
802
|
+
],
|
|
803
|
+
name: "RoleGranted",
|
|
804
|
+
type: "event"
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
anonymous: false,
|
|
808
|
+
inputs: [
|
|
809
|
+
{
|
|
810
|
+
indexed: true,
|
|
811
|
+
internalType: "bytes32",
|
|
812
|
+
name: "role",
|
|
813
|
+
type: "bytes32"
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
indexed: true,
|
|
817
|
+
internalType: "address",
|
|
818
|
+
name: "account",
|
|
819
|
+
type: "address"
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
indexed: true,
|
|
823
|
+
internalType: "address",
|
|
824
|
+
name: "sender",
|
|
825
|
+
type: "address"
|
|
826
|
+
}
|
|
827
|
+
],
|
|
828
|
+
name: "RoleRevoked",
|
|
829
|
+
type: "event"
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
anonymous: false,
|
|
833
|
+
inputs: [
|
|
834
|
+
{
|
|
835
|
+
indexed: true,
|
|
836
|
+
internalType: "bytes32",
|
|
837
|
+
name: "requestId",
|
|
838
|
+
type: "bytes32"
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
indexed: false,
|
|
842
|
+
internalType: "address",
|
|
843
|
+
name: "responder",
|
|
844
|
+
type: "address"
|
|
845
|
+
},
|
|
846
|
+
{
|
|
847
|
+
indexed: false,
|
|
848
|
+
internalType: "string",
|
|
849
|
+
name: "error",
|
|
850
|
+
type: "string"
|
|
851
|
+
}
|
|
852
|
+
],
|
|
853
|
+
name: "SignatureError",
|
|
854
|
+
type: "event"
|
|
855
|
+
},
|
|
856
|
+
{
|
|
857
|
+
anonymous: false,
|
|
858
|
+
inputs: [
|
|
859
|
+
{
|
|
860
|
+
indexed: false,
|
|
861
|
+
internalType: "address",
|
|
862
|
+
name: "sender",
|
|
863
|
+
type: "address"
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
indexed: false,
|
|
867
|
+
internalType: "bytes32",
|
|
868
|
+
name: "payload",
|
|
869
|
+
type: "bytes32"
|
|
870
|
+
},
|
|
871
|
+
{
|
|
872
|
+
indexed: false,
|
|
873
|
+
internalType: "uint32",
|
|
874
|
+
name: "keyVersion",
|
|
875
|
+
type: "uint32"
|
|
876
|
+
},
|
|
877
|
+
{
|
|
878
|
+
indexed: false,
|
|
879
|
+
internalType: "uint256",
|
|
880
|
+
name: "deposit",
|
|
881
|
+
type: "uint256"
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
indexed: false,
|
|
885
|
+
internalType: "uint256",
|
|
886
|
+
name: "chainId",
|
|
887
|
+
type: "uint256"
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
indexed: false,
|
|
891
|
+
internalType: "string",
|
|
892
|
+
name: "path",
|
|
893
|
+
type: "string"
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
indexed: false,
|
|
897
|
+
internalType: "string",
|
|
898
|
+
name: "algo",
|
|
899
|
+
type: "string"
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
indexed: false,
|
|
903
|
+
internalType: "string",
|
|
904
|
+
name: "dest",
|
|
905
|
+
type: "string"
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
indexed: false,
|
|
909
|
+
internalType: "string",
|
|
910
|
+
name: "params",
|
|
911
|
+
type: "string"
|
|
912
|
+
}
|
|
913
|
+
],
|
|
914
|
+
name: "SignatureRequested",
|
|
915
|
+
type: "event"
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
anonymous: false,
|
|
919
|
+
inputs: [
|
|
920
|
+
{
|
|
921
|
+
indexed: true,
|
|
922
|
+
internalType: "bytes32",
|
|
923
|
+
name: "requestId",
|
|
924
|
+
type: "bytes32"
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
indexed: false,
|
|
928
|
+
internalType: "address",
|
|
929
|
+
name: "responder",
|
|
930
|
+
type: "address"
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
components: [
|
|
934
|
+
{
|
|
935
|
+
components: [{
|
|
936
|
+
internalType: "uint256",
|
|
937
|
+
name: "x",
|
|
938
|
+
type: "uint256"
|
|
939
|
+
}, {
|
|
940
|
+
internalType: "uint256",
|
|
941
|
+
name: "y",
|
|
942
|
+
type: "uint256"
|
|
943
|
+
}],
|
|
944
|
+
internalType: "struct ChainSignatures.AffinePoint",
|
|
945
|
+
name: "bigR",
|
|
946
|
+
type: "tuple"
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
internalType: "uint256",
|
|
950
|
+
name: "s",
|
|
951
|
+
type: "uint256"
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
internalType: "uint8",
|
|
955
|
+
name: "recoveryId",
|
|
956
|
+
type: "uint8"
|
|
957
|
+
}
|
|
958
|
+
],
|
|
959
|
+
indexed: false,
|
|
960
|
+
internalType: "struct ChainSignatures.Signature",
|
|
961
|
+
name: "signature",
|
|
962
|
+
type: "tuple"
|
|
963
|
+
}
|
|
964
|
+
],
|
|
965
|
+
name: "SignatureResponded",
|
|
966
|
+
type: "event"
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
anonymous: false,
|
|
970
|
+
inputs: [{
|
|
971
|
+
indexed: true,
|
|
972
|
+
internalType: "address",
|
|
973
|
+
name: "owner",
|
|
974
|
+
type: "address"
|
|
975
|
+
}, {
|
|
976
|
+
indexed: false,
|
|
977
|
+
internalType: "uint256",
|
|
978
|
+
name: "amount",
|
|
979
|
+
type: "uint256"
|
|
980
|
+
}],
|
|
981
|
+
name: "Withdraw",
|
|
982
|
+
type: "event"
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
inputs: [],
|
|
986
|
+
name: "DEFAULT_ADMIN_ROLE",
|
|
987
|
+
outputs: [{
|
|
988
|
+
internalType: "bytes32",
|
|
989
|
+
name: "",
|
|
990
|
+
type: "bytes32"
|
|
991
|
+
}],
|
|
992
|
+
stateMutability: "view",
|
|
993
|
+
type: "function"
|
|
994
|
+
},
|
|
995
|
+
{
|
|
996
|
+
inputs: [{
|
|
997
|
+
internalType: "bytes32",
|
|
998
|
+
name: "role",
|
|
999
|
+
type: "bytes32"
|
|
1000
|
+
}],
|
|
1001
|
+
name: "getRoleAdmin",
|
|
1002
|
+
outputs: [{
|
|
1003
|
+
internalType: "bytes32",
|
|
1004
|
+
name: "",
|
|
1005
|
+
type: "bytes32"
|
|
1006
|
+
}],
|
|
1007
|
+
stateMutability: "view",
|
|
1008
|
+
type: "function"
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
inputs: [],
|
|
1012
|
+
name: "getSignatureDeposit",
|
|
1013
|
+
outputs: [{
|
|
1014
|
+
internalType: "uint256",
|
|
1015
|
+
name: "",
|
|
1016
|
+
type: "uint256"
|
|
1017
|
+
}],
|
|
1018
|
+
stateMutability: "view",
|
|
1019
|
+
type: "function"
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
inputs: [{
|
|
1023
|
+
internalType: "bytes32",
|
|
1024
|
+
name: "role",
|
|
1025
|
+
type: "bytes32"
|
|
1026
|
+
}, {
|
|
1027
|
+
internalType: "address",
|
|
1028
|
+
name: "account",
|
|
1029
|
+
type: "address"
|
|
1030
|
+
}],
|
|
1031
|
+
name: "grantRole",
|
|
1032
|
+
outputs: [],
|
|
1033
|
+
stateMutability: "nonpayable",
|
|
1034
|
+
type: "function"
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
inputs: [{
|
|
1038
|
+
internalType: "bytes32",
|
|
1039
|
+
name: "role",
|
|
1040
|
+
type: "bytes32"
|
|
1041
|
+
}, {
|
|
1042
|
+
internalType: "address",
|
|
1043
|
+
name: "account",
|
|
1044
|
+
type: "address"
|
|
1045
|
+
}],
|
|
1046
|
+
name: "hasRole",
|
|
1047
|
+
outputs: [{
|
|
1048
|
+
internalType: "bool",
|
|
1049
|
+
name: "",
|
|
1050
|
+
type: "bool"
|
|
1051
|
+
}],
|
|
1052
|
+
stateMutability: "view",
|
|
1053
|
+
type: "function"
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
inputs: [{
|
|
1057
|
+
internalType: "bytes32",
|
|
1058
|
+
name: "role",
|
|
1059
|
+
type: "bytes32"
|
|
1060
|
+
}, {
|
|
1061
|
+
internalType: "address",
|
|
1062
|
+
name: "callerConfirmation",
|
|
1063
|
+
type: "address"
|
|
1064
|
+
}],
|
|
1065
|
+
name: "renounceRole",
|
|
1066
|
+
outputs: [],
|
|
1067
|
+
stateMutability: "nonpayable",
|
|
1068
|
+
type: "function"
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
inputs: [{
|
|
1072
|
+
components: [{
|
|
1073
|
+
internalType: "bytes32",
|
|
1074
|
+
name: "requestId",
|
|
1075
|
+
type: "bytes32"
|
|
1076
|
+
}, {
|
|
1077
|
+
components: [
|
|
1078
|
+
{
|
|
1079
|
+
components: [{
|
|
1080
|
+
internalType: "uint256",
|
|
1081
|
+
name: "x",
|
|
1082
|
+
type: "uint256"
|
|
1083
|
+
}, {
|
|
1084
|
+
internalType: "uint256",
|
|
1085
|
+
name: "y",
|
|
1086
|
+
type: "uint256"
|
|
1087
|
+
}],
|
|
1088
|
+
internalType: "struct ChainSignatures.AffinePoint",
|
|
1089
|
+
name: "bigR",
|
|
1090
|
+
type: "tuple"
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
internalType: "uint256",
|
|
1094
|
+
name: "s",
|
|
1095
|
+
type: "uint256"
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
internalType: "uint8",
|
|
1099
|
+
name: "recoveryId",
|
|
1100
|
+
type: "uint8"
|
|
1101
|
+
}
|
|
1102
|
+
],
|
|
1103
|
+
internalType: "struct ChainSignatures.Signature",
|
|
1104
|
+
name: "signature",
|
|
1105
|
+
type: "tuple"
|
|
1106
|
+
}],
|
|
1107
|
+
internalType: "struct ChainSignatures.Response[]",
|
|
1108
|
+
name: "_responses",
|
|
1109
|
+
type: "tuple[]"
|
|
1110
|
+
}],
|
|
1111
|
+
name: "respond",
|
|
1112
|
+
outputs: [],
|
|
1113
|
+
stateMutability: "nonpayable",
|
|
1114
|
+
type: "function"
|
|
1115
|
+
},
|
|
1116
|
+
{
|
|
1117
|
+
inputs: [{
|
|
1118
|
+
components: [{
|
|
1119
|
+
internalType: "bytes32",
|
|
1120
|
+
name: "requestId",
|
|
1121
|
+
type: "bytes32"
|
|
1122
|
+
}, {
|
|
1123
|
+
internalType: "string",
|
|
1124
|
+
name: "errorMessage",
|
|
1125
|
+
type: "string"
|
|
1126
|
+
}],
|
|
1127
|
+
internalType: "struct ChainSignatures.ErrorResponse[]",
|
|
1128
|
+
name: "_errors",
|
|
1129
|
+
type: "tuple[]"
|
|
1130
|
+
}],
|
|
1131
|
+
name: "respondError",
|
|
1132
|
+
outputs: [],
|
|
1133
|
+
stateMutability: "nonpayable",
|
|
1134
|
+
type: "function"
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
inputs: [{
|
|
1138
|
+
internalType: "bytes32",
|
|
1139
|
+
name: "role",
|
|
1140
|
+
type: "bytes32"
|
|
1141
|
+
}, {
|
|
1142
|
+
internalType: "address",
|
|
1143
|
+
name: "account",
|
|
1144
|
+
type: "address"
|
|
1145
|
+
}],
|
|
1146
|
+
name: "revokeRole",
|
|
1147
|
+
outputs: [],
|
|
1148
|
+
stateMutability: "nonpayable",
|
|
1149
|
+
type: "function"
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
inputs: [{
|
|
1153
|
+
internalType: "uint256",
|
|
1154
|
+
name: "_amount",
|
|
1155
|
+
type: "uint256"
|
|
1156
|
+
}],
|
|
1157
|
+
name: "setSignatureDeposit",
|
|
1158
|
+
outputs: [],
|
|
1159
|
+
stateMutability: "nonpayable",
|
|
1160
|
+
type: "function"
|
|
1161
|
+
},
|
|
1162
|
+
{
|
|
1163
|
+
inputs: [{
|
|
1164
|
+
components: [
|
|
1165
|
+
{
|
|
1166
|
+
internalType: "bytes32",
|
|
1167
|
+
name: "payload",
|
|
1168
|
+
type: "bytes32"
|
|
1169
|
+
},
|
|
1170
|
+
{
|
|
1171
|
+
internalType: "string",
|
|
1172
|
+
name: "path",
|
|
1173
|
+
type: "string"
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
internalType: "uint32",
|
|
1177
|
+
name: "keyVersion",
|
|
1178
|
+
type: "uint32"
|
|
1179
|
+
},
|
|
1180
|
+
{
|
|
1181
|
+
internalType: "string",
|
|
1182
|
+
name: "algo",
|
|
1183
|
+
type: "string"
|
|
1184
|
+
},
|
|
1185
|
+
{
|
|
1186
|
+
internalType: "string",
|
|
1187
|
+
name: "dest",
|
|
1188
|
+
type: "string"
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
internalType: "string",
|
|
1192
|
+
name: "params",
|
|
1193
|
+
type: "string"
|
|
1194
|
+
}
|
|
1195
|
+
],
|
|
1196
|
+
internalType: "struct ChainSignatures.SignRequest",
|
|
1197
|
+
name: "_request",
|
|
1198
|
+
type: "tuple"
|
|
1199
|
+
}],
|
|
1200
|
+
name: "sign",
|
|
1201
|
+
outputs: [],
|
|
1202
|
+
stateMutability: "payable",
|
|
1203
|
+
type: "function"
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
inputs: [{
|
|
1207
|
+
internalType: "bytes4",
|
|
1208
|
+
name: "interfaceId",
|
|
1209
|
+
type: "bytes4"
|
|
1210
|
+
}],
|
|
1211
|
+
name: "supportsInterface",
|
|
1212
|
+
outputs: [{
|
|
1213
|
+
internalType: "bool",
|
|
1214
|
+
name: "",
|
|
1215
|
+
type: "bool"
|
|
1216
|
+
}],
|
|
1217
|
+
stateMutability: "view",
|
|
1218
|
+
type: "function"
|
|
1219
|
+
},
|
|
1220
|
+
{
|
|
1221
|
+
inputs: [{
|
|
1222
|
+
internalType: "uint256",
|
|
1223
|
+
name: "_amount",
|
|
1224
|
+
type: "uint256"
|
|
1225
|
+
}, {
|
|
1226
|
+
internalType: "address",
|
|
1227
|
+
name: "_receiver",
|
|
1228
|
+
type: "address"
|
|
1229
|
+
}],
|
|
1230
|
+
name: "withdraw",
|
|
1231
|
+
outputs: [],
|
|
1232
|
+
stateMutability: "nonpayable",
|
|
1233
|
+
type: "function"
|
|
1234
|
+
}
|
|
1235
|
+
];
|
|
1236
|
+
//#endregion
|
|
1237
|
+
//#region src/contracts/evm/errors.ts
|
|
1238
|
+
var errors_exports$1 = /* @__PURE__ */ __exportAll({
|
|
1239
|
+
ChainSignatureError: () => ChainSignatureError,
|
|
1240
|
+
SignatureContractError: () => SignatureContractError,
|
|
1241
|
+
SignatureNotFoundError: () => SignatureNotFoundError$1,
|
|
1242
|
+
SigningError: () => SigningError$1
|
|
1243
|
+
});
|
|
1244
|
+
var ChainSignatureError = class extends Error {
|
|
1245
|
+
requestId;
|
|
1246
|
+
receipt;
|
|
1247
|
+
constructor(message, requestId, receipt) {
|
|
1248
|
+
super(message);
|
|
1249
|
+
this.name = "ChainSignatureError";
|
|
1250
|
+
this.requestId = requestId;
|
|
1251
|
+
this.receipt = receipt;
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
1254
|
+
var SignatureNotFoundError$1 = class extends ChainSignatureError {
|
|
1255
|
+
constructor(requestId, receipt) {
|
|
1256
|
+
super("Signature not found after maximum retries", requestId, receipt);
|
|
1257
|
+
this.name = "SignatureNotFoundError";
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1260
|
+
var SignatureContractError = class extends ChainSignatureError {
|
|
1261
|
+
errorCode;
|
|
1262
|
+
constructor(errorCode, requestId, receipt) {
|
|
1263
|
+
super(`Signature error: ${errorCode}`, requestId, receipt);
|
|
1264
|
+
this.name = "SignatureContractError";
|
|
1265
|
+
this.errorCode = errorCode;
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
var SigningError$1 = class extends ChainSignatureError {
|
|
1269
|
+
originalError;
|
|
1270
|
+
constructor(requestId, receipt, originalError) {
|
|
1271
|
+
super("Error signing request", requestId, receipt);
|
|
1272
|
+
this.name = "SigningError";
|
|
1273
|
+
this.originalError = originalError;
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1276
|
+
//#endregion
|
|
1277
|
+
//#region src/utils/publicKey.ts
|
|
1278
|
+
const getRootPublicKey = (contractAddress, chain) => {
|
|
1279
|
+
const environment = Object.entries(CONTRACT_ADDRESSES[chain]).find(([_, address]) => address.toLowerCase() === contractAddress.toLowerCase())?.[0];
|
|
1280
|
+
if (environment) return ROOT_PUBLIC_KEYS[environment];
|
|
1281
|
+
};
|
|
1282
|
+
//#endregion
|
|
1283
|
+
//#region src/contracts/evm/utils.ts
|
|
1284
|
+
/**
|
|
1285
|
+
* Generates a unique request ID for EVM signature requests using keccak256 hashing.
|
|
1286
|
+
*
|
|
1287
|
+
* The request ID is computed by ABI-encoding the request parameters and hashing
|
|
1288
|
+
* the result. This ID is used to track signature requests and match them with
|
|
1289
|
+
* responses from the MPC network.
|
|
1290
|
+
*
|
|
1291
|
+
* @param request - The signature request parameters
|
|
1292
|
+
* @param request.address - The address of the requester (EVM address format)
|
|
1293
|
+
* @param request.payload - The data payload to be signed as a hex string
|
|
1294
|
+
* @param request.path - The derivation path for the signing key
|
|
1295
|
+
* @param request.keyVersion - The version of the signing key
|
|
1296
|
+
* @param request.chainId - The chain ID where the request originated
|
|
1297
|
+
* @param request.algo - The signing algorithm identifier
|
|
1298
|
+
* @param request.dest - The destination identifier for the signature
|
|
1299
|
+
* @param request.params - Additional parameters for the signing process
|
|
1300
|
+
* @returns The keccak256 hash of the encoded request as a hex string
|
|
1301
|
+
*
|
|
1302
|
+
* @example
|
|
1303
|
+
* ```typescript
|
|
1304
|
+
* const requestId = getRequestIdRespond({
|
|
1305
|
+
* address: '0x1234...abcd',
|
|
1306
|
+
* payload: '0xdeadbeef',
|
|
1307
|
+
* path: 'ethereum,1',
|
|
1308
|
+
* keyVersion: 0,
|
|
1309
|
+
* chainId: 1n,
|
|
1310
|
+
* algo: '',
|
|
1311
|
+
* dest: '',
|
|
1312
|
+
* params: '',
|
|
1313
|
+
* })
|
|
1314
|
+
* ```
|
|
1315
|
+
*/
|
|
1316
|
+
const getRequestIdRespond$1 = (request) => {
|
|
1317
|
+
return keccak256(encodeAbiParameters([
|
|
1318
|
+
{ type: "address" },
|
|
1319
|
+
{ type: "bytes" },
|
|
1320
|
+
{ type: "string" },
|
|
1321
|
+
{ type: "uint32" },
|
|
1322
|
+
{ type: "uint256" },
|
|
1323
|
+
{ type: "string" },
|
|
1324
|
+
{ type: "string" },
|
|
1325
|
+
{ type: "string" }
|
|
1326
|
+
], [
|
|
1327
|
+
request.address,
|
|
1328
|
+
request.payload,
|
|
1329
|
+
request.path,
|
|
1330
|
+
Number(request.keyVersion),
|
|
1331
|
+
request.chainId,
|
|
1332
|
+
request.algo,
|
|
1333
|
+
request.dest,
|
|
1334
|
+
request.params
|
|
1335
|
+
]));
|
|
1336
|
+
};
|
|
1337
|
+
//#endregion
|
|
1338
|
+
//#region src/contracts/evm/ChainSignaturesContract.ts
|
|
1339
|
+
/**
|
|
1340
|
+
* Implementation of the ChainSignatureContract for EVM chains.
|
|
1341
|
+
*
|
|
1342
|
+
* When signing data, the contract emits a SignatureRequested event with a requestId.
|
|
1343
|
+
* This requestId is used to track the signature request and retrieve the signature
|
|
1344
|
+
* once it's available. The sign method handles this process automatically by polling
|
|
1345
|
+
* for the signature using the requestId.
|
|
1346
|
+
*/
|
|
1347
|
+
var ChainSignatureContract$1 = class extends ChainSignatureContract$2 {
|
|
1348
|
+
publicClient;
|
|
1349
|
+
walletClient;
|
|
1350
|
+
contractAddress;
|
|
1351
|
+
rootPublicKey;
|
|
1352
|
+
/**
|
|
1353
|
+
* Creates a new instance of the ChainSignatureContract for EVM chains.
|
|
1354
|
+
*
|
|
1355
|
+
* @param args - Configuration options for the contract
|
|
1356
|
+
* @param args.publicClient - A Viem PublicClient instance for reading from the blockchain
|
|
1357
|
+
* @param args.walletClient - A Viem WalletClient instance for sending transactions
|
|
1358
|
+
* @param args.contractAddress - The address of the deployed ChainSignatures contract (e.g. `0x857ED3A242B59cC24144814a0DF41C397a3811E6`)
|
|
1359
|
+
* @param args.rootPublicKey - Optional root public key. If not provided, it will be derived from the contract address
|
|
1360
|
+
*/
|
|
1361
|
+
constructor(args) {
|
|
1362
|
+
super();
|
|
1363
|
+
this.publicClient = args.publicClient;
|
|
1364
|
+
this.walletClient = args.walletClient;
|
|
1365
|
+
this.contractAddress = args.contractAddress;
|
|
1366
|
+
const rootPublicKey = args.rootPublicKey || getRootPublicKey(this.contractAddress, CHAINS.ETHEREUM);
|
|
1367
|
+
if (!rootPublicKey) throw new Error(`Invalid public key, please provide a valid root public key or contract address`);
|
|
1368
|
+
this.rootPublicKey = normalizeToUncompressedPubKey(rootPublicKey);
|
|
1369
|
+
}
|
|
1370
|
+
async getCurrentSignatureDeposit() {
|
|
1371
|
+
return await this.publicClient.readContract({
|
|
1372
|
+
address: this.contractAddress,
|
|
1373
|
+
abi,
|
|
1374
|
+
functionName: "getSignatureDeposit"
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
async getDerivedPublicKey(args) {
|
|
1378
|
+
return deriveChildPublicKey(await this.getPublicKey(), args.predecessor.toLowerCase(), args.path, KDF_CHAIN_IDS.ETHEREUM, args.keyVersion);
|
|
1379
|
+
}
|
|
1380
|
+
async getPublicKey() {
|
|
1381
|
+
return this.rootPublicKey;
|
|
1382
|
+
}
|
|
1383
|
+
async getLatestKeyVersion() {
|
|
1384
|
+
const version = await this.publicClient.readContract({
|
|
1385
|
+
address: this.contractAddress,
|
|
1386
|
+
abi,
|
|
1387
|
+
functionName: "latestKeyVersion"
|
|
1388
|
+
});
|
|
1389
|
+
return Number(version);
|
|
1390
|
+
}
|
|
1391
|
+
/**
|
|
1392
|
+
* Sends a sign request transaction and return the transaction hash.
|
|
1393
|
+
*
|
|
1394
|
+
* @param args - The signature arguments
|
|
1395
|
+
* @param options - The signing options
|
|
1396
|
+
* @returns The transaction hash
|
|
1397
|
+
*/
|
|
1398
|
+
async createSignatureRequest(args, options = { sign: {
|
|
1399
|
+
algo: "",
|
|
1400
|
+
dest: "",
|
|
1401
|
+
params: ""
|
|
1402
|
+
} }) {
|
|
1403
|
+
if (!this.walletClient?.account) throw new Error("Wallet client required for signing operations");
|
|
1404
|
+
const requestParams = await this.getSignRequestParams(args, options.sign);
|
|
1405
|
+
const requestId = this.getRequestId(args, options.sign);
|
|
1406
|
+
return {
|
|
1407
|
+
txHash: await this.walletClient.sendTransaction({
|
|
1408
|
+
...options.transaction,
|
|
1409
|
+
account: this.walletClient.account,
|
|
1410
|
+
to: requestParams.target,
|
|
1411
|
+
data: requestParams.data,
|
|
1412
|
+
value: requestParams.value,
|
|
1413
|
+
chain: this.walletClient.chain
|
|
1414
|
+
}),
|
|
1415
|
+
requestId
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Sends a transaction to the contract to request a signature, then
|
|
1420
|
+
* polls for the signature result. If the signature is not found within the retry
|
|
1421
|
+
* parameters, it will throw an error.
|
|
1422
|
+
*/
|
|
1423
|
+
async sign(args, options = {
|
|
1424
|
+
sign: {
|
|
1425
|
+
algo: "",
|
|
1426
|
+
dest: "",
|
|
1427
|
+
params: ""
|
|
1428
|
+
},
|
|
1429
|
+
retry: {
|
|
1430
|
+
delay: 5e3,
|
|
1431
|
+
retryCount: 12
|
|
1432
|
+
}
|
|
1433
|
+
}) {
|
|
1434
|
+
const { txHash, requestId } = await this.createSignatureRequest(args, options);
|
|
1435
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
1436
|
+
try {
|
|
1437
|
+
const pollResult = await this.pollForRequestId({
|
|
1438
|
+
requestId,
|
|
1439
|
+
payload: args.payload,
|
|
1440
|
+
path: args.path,
|
|
1441
|
+
keyVersion: args.key_version,
|
|
1442
|
+
fromBlock: receipt.blockNumber,
|
|
1443
|
+
options: options.retry
|
|
1444
|
+
});
|
|
1445
|
+
if (!pollResult) throw new SignatureNotFoundError$1(requestId, receipt);
|
|
1446
|
+
if ("error" in pollResult) throw new SignatureContractError(pollResult.error, requestId, receipt);
|
|
1447
|
+
return pollResult;
|
|
1448
|
+
} catch (error) {
|
|
1449
|
+
if (error instanceof SignatureNotFoundError$1 || error instanceof SignatureContractError) throw error;
|
|
1450
|
+
else throw new SigningError$1(requestId, receipt, error instanceof Error ? error : void 0);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
async pollForRequestId({ requestId, payload, path, keyVersion, fromBlock, options }) {
|
|
1454
|
+
const delay = options?.delay ?? 5e3;
|
|
1455
|
+
const retryCount = options?.retryCount ?? 12;
|
|
1456
|
+
const result = await withRetry(async () => {
|
|
1457
|
+
const result = await this.getSignatureFromEvents(requestId, fromBlock);
|
|
1458
|
+
if (result) {
|
|
1459
|
+
if (!await verifyRecoveredAddress(result, payload, this.walletClient.account?.address, path, this, keyVersion)) throw new Error("Signature not found yet");
|
|
1460
|
+
return result;
|
|
1461
|
+
} else throw new Error("Signature not found yet");
|
|
1462
|
+
}, {
|
|
1463
|
+
delay,
|
|
1464
|
+
retryCount,
|
|
1465
|
+
shouldRetry: ({ count, error }) => {
|
|
1466
|
+
console.log(`Retrying get signature: ${count}/${retryCount}`);
|
|
1467
|
+
return error.message === "Signature not found yet";
|
|
1468
|
+
}
|
|
1469
|
+
});
|
|
1470
|
+
if (result) return result;
|
|
1471
|
+
return await this.getErrorFromEvents(requestId, fromBlock);
|
|
1472
|
+
}
|
|
1473
|
+
async getSignRequestParams(args, options = {
|
|
1474
|
+
algo: "",
|
|
1475
|
+
dest: "",
|
|
1476
|
+
params: ""
|
|
1477
|
+
}) {
|
|
1478
|
+
const request = {
|
|
1479
|
+
payload: bytesToHex(new Uint8Array(args.payload)),
|
|
1480
|
+
path: args.path,
|
|
1481
|
+
keyVersion: args.key_version,
|
|
1482
|
+
algo: options.algo ?? "",
|
|
1483
|
+
dest: options.dest ?? "",
|
|
1484
|
+
params: options.params ?? ""
|
|
1485
|
+
};
|
|
1486
|
+
return {
|
|
1487
|
+
target: this.contractAddress,
|
|
1488
|
+
data: encodeFunctionData({
|
|
1489
|
+
abi,
|
|
1490
|
+
functionName: "sign",
|
|
1491
|
+
args: [request]
|
|
1492
|
+
}),
|
|
1493
|
+
value: await this.getCurrentSignatureDeposit()
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
/**
|
|
1497
|
+
* Generates the request ID for a signature request allowing to track the response.
|
|
1498
|
+
*
|
|
1499
|
+
* @param args - The signature request object containing:
|
|
1500
|
+
* @param args.payload - The data payload to be signed as a hex string
|
|
1501
|
+
* @param args.path - The derivation path for the key
|
|
1502
|
+
* @param args.keyVersion - The version of the key to use
|
|
1503
|
+
* @param options - The signature request object containing:
|
|
1504
|
+
* @param options.algo - The signing algorithm to use
|
|
1505
|
+
* @param options.dest - The destination for the signature
|
|
1506
|
+
* @param options.params - Additional parameters for the signing process
|
|
1507
|
+
* @returns A hex string representing the unique request ID
|
|
1508
|
+
*
|
|
1509
|
+
* @example
|
|
1510
|
+
* ```typescript
|
|
1511
|
+
* const requestId = ChainSignatureContract.getRequestId({
|
|
1512
|
+
* payload: bytesToHex(new Uint8Array(args.payload)),
|
|
1513
|
+
* path: '',
|
|
1514
|
+
* keyVersion: 0
|
|
1515
|
+
* });
|
|
1516
|
+
* console.log(requestId); // 0x...
|
|
1517
|
+
* ```
|
|
1518
|
+
*/
|
|
1519
|
+
getRequestId(args, options = {
|
|
1520
|
+
algo: "",
|
|
1521
|
+
dest: "",
|
|
1522
|
+
params: ""
|
|
1523
|
+
}) {
|
|
1524
|
+
if (!this.walletClient.account) throw new Error("Wallet client account required to compute requestId");
|
|
1525
|
+
if (!this.publicClient.chain?.id) throw new Error("Public client chain required to compute requestId");
|
|
1526
|
+
return getRequestIdRespond$1({
|
|
1527
|
+
payload: bytesToHex(new Uint8Array(args.payload)),
|
|
1528
|
+
path: args.path,
|
|
1529
|
+
keyVersion: args.key_version,
|
|
1530
|
+
algo: options.algo ?? "",
|
|
1531
|
+
dest: options.dest ?? "",
|
|
1532
|
+
params: options.params ?? "",
|
|
1533
|
+
address: this.walletClient.account.address,
|
|
1534
|
+
chainId: BigInt(this.publicClient.chain.id)
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
async getErrorFromEvents(requestId, fromBlock) {
|
|
1538
|
+
const errorLogs = await this.publicClient.getContractEvents({
|
|
1539
|
+
address: this.contractAddress,
|
|
1540
|
+
abi,
|
|
1541
|
+
eventName: "SignatureError",
|
|
1542
|
+
args: { requestId },
|
|
1543
|
+
fromBlock,
|
|
1544
|
+
toBlock: "latest"
|
|
1545
|
+
});
|
|
1546
|
+
if (errorLogs.length > 0) {
|
|
1547
|
+
const { args: errorData } = errorLogs[errorLogs.length - 1];
|
|
1548
|
+
return errorData;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
/**
|
|
1552
|
+
* Searches for SignatureResponded events that match the given requestId.
|
|
1553
|
+
* It works in conjunction with the getRequestId method which generates the unique
|
|
1554
|
+
* identifier for a signature request.
|
|
1555
|
+
*
|
|
1556
|
+
* @param requestId - The identifier for the signature request
|
|
1557
|
+
* @param fromBlock - The block number to start searching from
|
|
1558
|
+
* @returns The RSV signature if found, undefined otherwise
|
|
1559
|
+
*/
|
|
1560
|
+
async getSignatureFromEvents(requestId, fromBlock) {
|
|
1561
|
+
const logs = await this.publicClient.getContractEvents({
|
|
1562
|
+
address: this.contractAddress,
|
|
1563
|
+
abi,
|
|
1564
|
+
eventName: "SignatureResponded",
|
|
1565
|
+
args: { requestId },
|
|
1566
|
+
fromBlock,
|
|
1567
|
+
toBlock: "latest"
|
|
1568
|
+
});
|
|
1569
|
+
if (logs.length > 0) {
|
|
1570
|
+
const { args: signatureData } = logs[logs.length - 1];
|
|
1571
|
+
return toRSV(signatureData.signature);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
};
|
|
1575
|
+
//#endregion
|
|
1576
|
+
//#region src/contracts/evm/index.ts
|
|
1577
|
+
var evm_exports = /* @__PURE__ */ __exportAll({
|
|
1578
|
+
ChainSignatureContract: () => ChainSignatureContract$1,
|
|
1579
|
+
getRequestIdRespond: () => getRequestIdRespond$1,
|
|
1580
|
+
utils: () => utils$1
|
|
1581
|
+
});
|
|
1582
|
+
const utils$1 = {
|
|
1583
|
+
ChainSignaturesContractABI: ChainSignaturesContractABI_exports,
|
|
1584
|
+
errors: errors_exports$1
|
|
1585
|
+
};
|
|
1586
|
+
//#endregion
|
|
1587
|
+
//#region src/contracts/solana/errors.ts
|
|
1588
|
+
var errors_exports = /* @__PURE__ */ __exportAll({
|
|
1589
|
+
ResponseError: () => ResponseError,
|
|
1590
|
+
SignatureNotFoundError: () => SignatureNotFoundError,
|
|
1591
|
+
SigningError: () => SigningError
|
|
1592
|
+
});
|
|
1593
|
+
var SignatureNotFoundError = class extends Error {
|
|
1594
|
+
requestId;
|
|
1595
|
+
hash;
|
|
1596
|
+
constructor(requestId, metadata) {
|
|
1597
|
+
const message = requestId ? `Signature not found for request ID: ${requestId}` : "Signature not found";
|
|
1598
|
+
super(message);
|
|
1599
|
+
this.name = "SignatureNotFoundError";
|
|
1600
|
+
this.requestId = requestId;
|
|
1601
|
+
this.hash = metadata?.hash;
|
|
1602
|
+
}
|
|
1603
|
+
};
|
|
1604
|
+
var SigningError = class extends Error {
|
|
1605
|
+
requestId;
|
|
1606
|
+
hash;
|
|
1607
|
+
originalError;
|
|
1608
|
+
constructor(requestId, metadata, originalError) {
|
|
1609
|
+
super(`Signing error for request ID: ${requestId}`);
|
|
1610
|
+
this.name = "SigningError";
|
|
1611
|
+
this.requestId = requestId;
|
|
1612
|
+
this.hash = metadata?.hash;
|
|
1613
|
+
this.originalError = originalError;
|
|
1614
|
+
}
|
|
1615
|
+
};
|
|
1616
|
+
var ResponseError = class extends Error {
|
|
1617
|
+
constructor(message) {
|
|
1618
|
+
super(message);
|
|
1619
|
+
this.name = "ResponseError";
|
|
1620
|
+
}
|
|
1621
|
+
};
|
|
1622
|
+
//#endregion
|
|
1623
|
+
//#region src/contracts/solana/types/chain_signatures_project.json
|
|
1624
|
+
var chain_signatures_project_exports = /* @__PURE__ */ __exportAll({
|
|
1625
|
+
accounts: () => accounts,
|
|
1626
|
+
address: () => address,
|
|
1627
|
+
default: () => chain_signatures_project_default,
|
|
1628
|
+
errors: () => errors,
|
|
1629
|
+
events: () => events,
|
|
1630
|
+
instructions: () => instructions,
|
|
1631
|
+
metadata: () => metadata,
|
|
1632
|
+
types: () => types
|
|
1633
|
+
});
|
|
1634
|
+
var address = "H5tHfpYoEnarrrzcV7sWBcZhiKMvL2aRpUYvb1ydWkwS";
|
|
1635
|
+
var metadata = {
|
|
1636
|
+
"name": "chain_signatures",
|
|
1637
|
+
"version": "0.4.0",
|
|
1638
|
+
"spec": "0.1.0",
|
|
1639
|
+
"description": "Chain signatures program for cross-chain signing on Solana",
|
|
1640
|
+
"repository": "https://github.com/sig-net/signet-solana-program"
|
|
1641
|
+
};
|
|
1642
|
+
var instructions = [
|
|
1643
|
+
{
|
|
1644
|
+
"name": "get_signature_deposit",
|
|
1645
|
+
"docs": ["* @dev Function to get the current signature deposit amount.\n * @return The current signature deposit amount."],
|
|
1646
|
+
"discriminator": [
|
|
1647
|
+
45,
|
|
1648
|
+
243,
|
|
1649
|
+
86,
|
|
1650
|
+
86,
|
|
1651
|
+
58,
|
|
1652
|
+
57,
|
|
1653
|
+
172,
|
|
1654
|
+
253
|
|
1655
|
+
],
|
|
1656
|
+
"accounts": [{
|
|
1657
|
+
"name": "program_state",
|
|
1658
|
+
"pda": { "seeds": [{
|
|
1659
|
+
"kind": "const",
|
|
1660
|
+
"value": [
|
|
1661
|
+
112,
|
|
1662
|
+
114,
|
|
1663
|
+
111,
|
|
1664
|
+
103,
|
|
1665
|
+
114,
|
|
1666
|
+
97,
|
|
1667
|
+
109,
|
|
1668
|
+
45,
|
|
1669
|
+
115,
|
|
1670
|
+
116,
|
|
1671
|
+
97,
|
|
1672
|
+
116,
|
|
1673
|
+
101
|
|
1674
|
+
]
|
|
1675
|
+
}] }
|
|
1676
|
+
}],
|
|
1677
|
+
"args": [],
|
|
1678
|
+
"returns": "u64"
|
|
1679
|
+
},
|
|
1680
|
+
{
|
|
1681
|
+
"name": "initialize",
|
|
1682
|
+
"docs": ["* @dev Function to initialize the program state.\n * @param signature_deposit The deposit required for signature requests.\n * @param chain_id The CAIP-2 chain identifier."],
|
|
1683
|
+
"discriminator": [
|
|
1684
|
+
175,
|
|
1685
|
+
175,
|
|
1686
|
+
109,
|
|
1687
|
+
31,
|
|
1688
|
+
13,
|
|
1689
|
+
152,
|
|
1690
|
+
155,
|
|
1691
|
+
237
|
|
1692
|
+
],
|
|
1693
|
+
"accounts": [
|
|
1694
|
+
{
|
|
1695
|
+
"name": "program_state",
|
|
1696
|
+
"writable": true,
|
|
1697
|
+
"pda": { "seeds": [{
|
|
1698
|
+
"kind": "const",
|
|
1699
|
+
"value": [
|
|
1700
|
+
112,
|
|
1701
|
+
114,
|
|
1702
|
+
111,
|
|
1703
|
+
103,
|
|
1704
|
+
114,
|
|
1705
|
+
97,
|
|
1706
|
+
109,
|
|
1707
|
+
45,
|
|
1708
|
+
115,
|
|
1709
|
+
116,
|
|
1710
|
+
97,
|
|
1711
|
+
116,
|
|
1712
|
+
101
|
|
1713
|
+
]
|
|
1714
|
+
}] }
|
|
1715
|
+
},
|
|
1716
|
+
{
|
|
1717
|
+
"name": "admin",
|
|
1718
|
+
"writable": true,
|
|
1719
|
+
"signer": true
|
|
1720
|
+
},
|
|
1721
|
+
{
|
|
1722
|
+
"name": "system_program",
|
|
1723
|
+
"address": "11111111111111111111111111111111"
|
|
1724
|
+
}
|
|
1725
|
+
],
|
|
1726
|
+
"args": [{
|
|
1727
|
+
"name": "signature_deposit",
|
|
1728
|
+
"type": "u64"
|
|
1729
|
+
}, {
|
|
1730
|
+
"name": "chain_id",
|
|
1731
|
+
"type": "string"
|
|
1732
|
+
}]
|
|
1733
|
+
},
|
|
1734
|
+
{
|
|
1735
|
+
"name": "respond",
|
|
1736
|
+
"docs": ["* @dev Function to respond to signature requests.\n * @param request_ids The array of request IDs.\n * @param signatures The array of signature responses.\n * @notice When multiple entries reuse a request id, events emit in canonical signature order (PSBT-style)."],
|
|
1737
|
+
"discriminator": [
|
|
1738
|
+
72,
|
|
1739
|
+
65,
|
|
1740
|
+
227,
|
|
1741
|
+
97,
|
|
1742
|
+
42,
|
|
1743
|
+
255,
|
|
1744
|
+
147,
|
|
1745
|
+
12
|
|
1746
|
+
],
|
|
1747
|
+
"accounts": [
|
|
1748
|
+
{
|
|
1749
|
+
"name": "responder",
|
|
1750
|
+
"signer": true
|
|
1751
|
+
},
|
|
1752
|
+
{
|
|
1753
|
+
"name": "event_authority",
|
|
1754
|
+
"pda": { "seeds": [{
|
|
1755
|
+
"kind": "const",
|
|
1756
|
+
"value": [
|
|
1757
|
+
95,
|
|
1758
|
+
95,
|
|
1759
|
+
101,
|
|
1760
|
+
118,
|
|
1761
|
+
101,
|
|
1762
|
+
110,
|
|
1763
|
+
116,
|
|
1764
|
+
95,
|
|
1765
|
+
97,
|
|
1766
|
+
117,
|
|
1767
|
+
116,
|
|
1768
|
+
104,
|
|
1769
|
+
111,
|
|
1770
|
+
114,
|
|
1771
|
+
105,
|
|
1772
|
+
116,
|
|
1773
|
+
121
|
|
1774
|
+
]
|
|
1775
|
+
}] }
|
|
1776
|
+
},
|
|
1777
|
+
{ "name": "program" }
|
|
1778
|
+
],
|
|
1779
|
+
"args": [{
|
|
1780
|
+
"name": "request_ids",
|
|
1781
|
+
"type": { "vec": { "array": ["u8", 32] } }
|
|
1782
|
+
}, {
|
|
1783
|
+
"name": "signatures",
|
|
1784
|
+
"type": { "vec": { "defined": { "name": "Signature" } } }
|
|
1785
|
+
}]
|
|
1786
|
+
},
|
|
1787
|
+
{
|
|
1788
|
+
"name": "respond_bidirectional",
|
|
1789
|
+
"docs": ["* @dev Function to finalize bidirectional flow\n * @param request_id The ID of the signature request to respond to\n * @param serialized_output output of the previously executed transaction\n * @param signature ECDSA signature of the serialized output and request_id (keccak256(request_id.concat(serialized_output)))"],
|
|
1790
|
+
"discriminator": [
|
|
1791
|
+
138,
|
|
1792
|
+
0,
|
|
1793
|
+
45,
|
|
1794
|
+
246,
|
|
1795
|
+
236,
|
|
1796
|
+
211,
|
|
1797
|
+
109,
|
|
1798
|
+
81
|
|
1799
|
+
],
|
|
1800
|
+
"accounts": [{
|
|
1801
|
+
"name": "responder",
|
|
1802
|
+
"signer": true
|
|
1803
|
+
}],
|
|
1804
|
+
"args": [
|
|
1805
|
+
{
|
|
1806
|
+
"name": "request_id",
|
|
1807
|
+
"type": { "array": ["u8", 32] }
|
|
1808
|
+
},
|
|
1809
|
+
{
|
|
1810
|
+
"name": "serialized_output",
|
|
1811
|
+
"type": "bytes"
|
|
1812
|
+
},
|
|
1813
|
+
{
|
|
1814
|
+
"name": "signature",
|
|
1815
|
+
"type": { "defined": { "name": "Signature" } }
|
|
1816
|
+
}
|
|
1817
|
+
]
|
|
1818
|
+
},
|
|
1819
|
+
{
|
|
1820
|
+
"name": "respond_error",
|
|
1821
|
+
"docs": ["* @dev Function to emit signature generation errors.\n * @param errors The array of signature generation errors."],
|
|
1822
|
+
"discriminator": [
|
|
1823
|
+
3,
|
|
1824
|
+
170,
|
|
1825
|
+
41,
|
|
1826
|
+
132,
|
|
1827
|
+
72,
|
|
1828
|
+
184,
|
|
1829
|
+
252,
|
|
1830
|
+
69
|
|
1831
|
+
],
|
|
1832
|
+
"accounts": [{
|
|
1833
|
+
"name": "responder",
|
|
1834
|
+
"signer": true
|
|
1835
|
+
}],
|
|
1836
|
+
"args": [{
|
|
1837
|
+
"name": "errors",
|
|
1838
|
+
"type": { "vec": { "defined": { "name": "ErrorResponse" } } }
|
|
1839
|
+
}]
|
|
1840
|
+
},
|
|
1841
|
+
{
|
|
1842
|
+
"name": "sign",
|
|
1843
|
+
"docs": ["* @dev Function to request a signature.\n * @param payload The payload to be signed.\n * @param key_version The version of the key used for signing.\n * @param path The derivation path for the user account.\n * @param algo The algorithm used for signing.\n * @param dest The response destination.\n * @param params Additional parameters."],
|
|
1844
|
+
"discriminator": [
|
|
1845
|
+
5,
|
|
1846
|
+
221,
|
|
1847
|
+
155,
|
|
1848
|
+
46,
|
|
1849
|
+
237,
|
|
1850
|
+
91,
|
|
1851
|
+
28,
|
|
1852
|
+
236
|
|
1853
|
+
],
|
|
1854
|
+
"accounts": [
|
|
1855
|
+
{
|
|
1856
|
+
"name": "program_state",
|
|
1857
|
+
"writable": true,
|
|
1858
|
+
"pda": { "seeds": [{
|
|
1859
|
+
"kind": "const",
|
|
1860
|
+
"value": [
|
|
1861
|
+
112,
|
|
1862
|
+
114,
|
|
1863
|
+
111,
|
|
1864
|
+
103,
|
|
1865
|
+
114,
|
|
1866
|
+
97,
|
|
1867
|
+
109,
|
|
1868
|
+
45,
|
|
1869
|
+
115,
|
|
1870
|
+
116,
|
|
1871
|
+
97,
|
|
1872
|
+
116,
|
|
1873
|
+
101
|
|
1874
|
+
]
|
|
1875
|
+
}] }
|
|
1876
|
+
},
|
|
1877
|
+
{
|
|
1878
|
+
"name": "requester",
|
|
1879
|
+
"writable": true,
|
|
1880
|
+
"signer": true
|
|
1881
|
+
},
|
|
1882
|
+
{
|
|
1883
|
+
"name": "fee_payer",
|
|
1884
|
+
"writable": true,
|
|
1885
|
+
"signer": true,
|
|
1886
|
+
"optional": true
|
|
1887
|
+
},
|
|
1888
|
+
{
|
|
1889
|
+
"name": "system_program",
|
|
1890
|
+
"address": "11111111111111111111111111111111"
|
|
1891
|
+
},
|
|
1892
|
+
{
|
|
1893
|
+
"name": "event_authority",
|
|
1894
|
+
"pda": { "seeds": [{
|
|
1895
|
+
"kind": "const",
|
|
1896
|
+
"value": [
|
|
1897
|
+
95,
|
|
1898
|
+
95,
|
|
1899
|
+
101,
|
|
1900
|
+
118,
|
|
1901
|
+
101,
|
|
1902
|
+
110,
|
|
1903
|
+
116,
|
|
1904
|
+
95,
|
|
1905
|
+
97,
|
|
1906
|
+
117,
|
|
1907
|
+
116,
|
|
1908
|
+
104,
|
|
1909
|
+
111,
|
|
1910
|
+
114,
|
|
1911
|
+
105,
|
|
1912
|
+
116,
|
|
1913
|
+
121
|
|
1914
|
+
]
|
|
1915
|
+
}] }
|
|
1916
|
+
},
|
|
1917
|
+
{ "name": "program" }
|
|
1918
|
+
],
|
|
1919
|
+
"args": [
|
|
1920
|
+
{
|
|
1921
|
+
"name": "payload",
|
|
1922
|
+
"type": { "array": ["u8", 32] }
|
|
1923
|
+
},
|
|
1924
|
+
{
|
|
1925
|
+
"name": "key_version",
|
|
1926
|
+
"type": "u32"
|
|
1927
|
+
},
|
|
1928
|
+
{
|
|
1929
|
+
"name": "path",
|
|
1930
|
+
"type": "string"
|
|
1931
|
+
},
|
|
1932
|
+
{
|
|
1933
|
+
"name": "algo",
|
|
1934
|
+
"type": "string"
|
|
1935
|
+
},
|
|
1936
|
+
{
|
|
1937
|
+
"name": "dest",
|
|
1938
|
+
"type": "string"
|
|
1939
|
+
},
|
|
1940
|
+
{
|
|
1941
|
+
"name": "params",
|
|
1942
|
+
"type": "string"
|
|
1943
|
+
}
|
|
1944
|
+
]
|
|
1945
|
+
},
|
|
1946
|
+
{
|
|
1947
|
+
"name": "sign_bidirectional",
|
|
1948
|
+
"docs": ["* @dev Function to initiate bidirectional flow\n * @param serialized_transaction transaction to be signed\n * @param caip2_id chain identifier\n * @param key_version The version of the key used for signing.\n * @param path The derivation path for the user account.\n * @param algo The algorithm used for signing.\n * @param dest The response destination.\n * @param params Additional parameters.\n * @param program_id Program ID to callback after execution (not yet enabled).\n * @param output_deserialization_schema schema for transaction output deserialization\n * @param respond_serialization_schema serialization schema for respond_bidirectional payload"],
|
|
1949
|
+
"discriminator": [
|
|
1950
|
+
21,
|
|
1951
|
+
104,
|
|
1952
|
+
182,
|
|
1953
|
+
213,
|
|
1954
|
+
189,
|
|
1955
|
+
143,
|
|
1956
|
+
219,
|
|
1957
|
+
48
|
|
1958
|
+
],
|
|
1959
|
+
"accounts": [
|
|
1960
|
+
{
|
|
1961
|
+
"name": "program_state",
|
|
1962
|
+
"writable": true,
|
|
1963
|
+
"pda": { "seeds": [{
|
|
1964
|
+
"kind": "const",
|
|
1965
|
+
"value": [
|
|
1966
|
+
112,
|
|
1967
|
+
114,
|
|
1968
|
+
111,
|
|
1969
|
+
103,
|
|
1970
|
+
114,
|
|
1971
|
+
97,
|
|
1972
|
+
109,
|
|
1973
|
+
45,
|
|
1974
|
+
115,
|
|
1975
|
+
116,
|
|
1976
|
+
97,
|
|
1977
|
+
116,
|
|
1978
|
+
101
|
|
1979
|
+
]
|
|
1980
|
+
}] }
|
|
1981
|
+
},
|
|
1982
|
+
{
|
|
1983
|
+
"name": "requester",
|
|
1984
|
+
"writable": true,
|
|
1985
|
+
"signer": true
|
|
1986
|
+
},
|
|
1987
|
+
{
|
|
1988
|
+
"name": "fee_payer",
|
|
1989
|
+
"writable": true,
|
|
1990
|
+
"signer": true,
|
|
1991
|
+
"optional": true
|
|
1992
|
+
},
|
|
1993
|
+
{
|
|
1994
|
+
"name": "system_program",
|
|
1995
|
+
"address": "11111111111111111111111111111111"
|
|
1996
|
+
},
|
|
1997
|
+
{
|
|
1998
|
+
"name": "instructions",
|
|
1999
|
+
"optional": true
|
|
2000
|
+
},
|
|
2001
|
+
{
|
|
2002
|
+
"name": "event_authority",
|
|
2003
|
+
"pda": { "seeds": [{
|
|
2004
|
+
"kind": "const",
|
|
2005
|
+
"value": [
|
|
2006
|
+
95,
|
|
2007
|
+
95,
|
|
2008
|
+
101,
|
|
2009
|
+
118,
|
|
2010
|
+
101,
|
|
2011
|
+
110,
|
|
2012
|
+
116,
|
|
2013
|
+
95,
|
|
2014
|
+
97,
|
|
2015
|
+
117,
|
|
2016
|
+
116,
|
|
2017
|
+
104,
|
|
2018
|
+
111,
|
|
2019
|
+
114,
|
|
2020
|
+
105,
|
|
2021
|
+
116,
|
|
2022
|
+
121
|
|
2023
|
+
]
|
|
2024
|
+
}] }
|
|
2025
|
+
},
|
|
2026
|
+
{ "name": "program" }
|
|
2027
|
+
],
|
|
2028
|
+
"args": [
|
|
2029
|
+
{
|
|
2030
|
+
"name": "serialized_transaction",
|
|
2031
|
+
"type": "bytes"
|
|
2032
|
+
},
|
|
2033
|
+
{
|
|
2034
|
+
"name": "caip2_id",
|
|
2035
|
+
"type": "string"
|
|
2036
|
+
},
|
|
2037
|
+
{
|
|
2038
|
+
"name": "key_version",
|
|
2039
|
+
"type": "u32"
|
|
2040
|
+
},
|
|
2041
|
+
{
|
|
2042
|
+
"name": "path",
|
|
2043
|
+
"type": "string"
|
|
2044
|
+
},
|
|
2045
|
+
{
|
|
2046
|
+
"name": "algo",
|
|
2047
|
+
"type": "string"
|
|
2048
|
+
},
|
|
2049
|
+
{
|
|
2050
|
+
"name": "dest",
|
|
2051
|
+
"type": "string"
|
|
2052
|
+
},
|
|
2053
|
+
{
|
|
2054
|
+
"name": "params",
|
|
2055
|
+
"type": "string"
|
|
2056
|
+
},
|
|
2057
|
+
{
|
|
2058
|
+
"name": "program_id",
|
|
2059
|
+
"type": "pubkey"
|
|
2060
|
+
},
|
|
2061
|
+
{
|
|
2062
|
+
"name": "output_deserialization_schema",
|
|
2063
|
+
"type": "bytes"
|
|
2064
|
+
},
|
|
2065
|
+
{
|
|
2066
|
+
"name": "respond_serialization_schema",
|
|
2067
|
+
"type": "bytes"
|
|
2068
|
+
}
|
|
2069
|
+
]
|
|
2070
|
+
},
|
|
2071
|
+
{
|
|
2072
|
+
"name": "update_deposit",
|
|
2073
|
+
"docs": ["* @dev Function to set the signature deposit amount.\n * @param new_deposit The new deposit amount."],
|
|
2074
|
+
"discriminator": [
|
|
2075
|
+
126,
|
|
2076
|
+
116,
|
|
2077
|
+
15,
|
|
2078
|
+
164,
|
|
2079
|
+
238,
|
|
2080
|
+
179,
|
|
2081
|
+
155,
|
|
2082
|
+
59
|
|
2083
|
+
],
|
|
2084
|
+
"accounts": [
|
|
2085
|
+
{
|
|
2086
|
+
"name": "program_state",
|
|
2087
|
+
"writable": true,
|
|
2088
|
+
"pda": { "seeds": [{
|
|
2089
|
+
"kind": "const",
|
|
2090
|
+
"value": [
|
|
2091
|
+
112,
|
|
2092
|
+
114,
|
|
2093
|
+
111,
|
|
2094
|
+
103,
|
|
2095
|
+
114,
|
|
2096
|
+
97,
|
|
2097
|
+
109,
|
|
2098
|
+
45,
|
|
2099
|
+
115,
|
|
2100
|
+
116,
|
|
2101
|
+
97,
|
|
2102
|
+
116,
|
|
2103
|
+
101
|
|
2104
|
+
]
|
|
2105
|
+
}] }
|
|
2106
|
+
},
|
|
2107
|
+
{
|
|
2108
|
+
"name": "admin",
|
|
2109
|
+
"writable": true,
|
|
2110
|
+
"signer": true,
|
|
2111
|
+
"relations": ["program_state"]
|
|
2112
|
+
},
|
|
2113
|
+
{
|
|
2114
|
+
"name": "system_program",
|
|
2115
|
+
"address": "11111111111111111111111111111111"
|
|
2116
|
+
}
|
|
2117
|
+
],
|
|
2118
|
+
"args": [{
|
|
2119
|
+
"name": "new_deposit",
|
|
2120
|
+
"type": "u64"
|
|
2121
|
+
}]
|
|
2122
|
+
},
|
|
2123
|
+
{
|
|
2124
|
+
"name": "withdraw_funds",
|
|
2125
|
+
"docs": ["* @dev Function to withdraw funds from the program.\n * @param amount The amount to withdraw."],
|
|
2126
|
+
"discriminator": [
|
|
2127
|
+
241,
|
|
2128
|
+
36,
|
|
2129
|
+
29,
|
|
2130
|
+
111,
|
|
2131
|
+
208,
|
|
2132
|
+
31,
|
|
2133
|
+
104,
|
|
2134
|
+
217
|
|
2135
|
+
],
|
|
2136
|
+
"accounts": [
|
|
2137
|
+
{
|
|
2138
|
+
"name": "program_state",
|
|
2139
|
+
"writable": true,
|
|
2140
|
+
"pda": { "seeds": [{
|
|
2141
|
+
"kind": "const",
|
|
2142
|
+
"value": [
|
|
2143
|
+
112,
|
|
2144
|
+
114,
|
|
2145
|
+
111,
|
|
2146
|
+
103,
|
|
2147
|
+
114,
|
|
2148
|
+
97,
|
|
2149
|
+
109,
|
|
2150
|
+
45,
|
|
2151
|
+
115,
|
|
2152
|
+
116,
|
|
2153
|
+
97,
|
|
2154
|
+
116,
|
|
2155
|
+
101
|
|
2156
|
+
]
|
|
2157
|
+
}] }
|
|
2158
|
+
},
|
|
2159
|
+
{
|
|
2160
|
+
"name": "admin",
|
|
2161
|
+
"writable": true,
|
|
2162
|
+
"signer": true,
|
|
2163
|
+
"relations": ["program_state"]
|
|
2164
|
+
},
|
|
2165
|
+
{
|
|
2166
|
+
"name": "recipient",
|
|
2167
|
+
"docs": ["function by checking it is not the zero address."],
|
|
2168
|
+
"writable": true
|
|
2169
|
+
},
|
|
2170
|
+
{
|
|
2171
|
+
"name": "system_program",
|
|
2172
|
+
"address": "11111111111111111111111111111111"
|
|
2173
|
+
}
|
|
2174
|
+
],
|
|
2175
|
+
"args": [{
|
|
2176
|
+
"name": "amount",
|
|
2177
|
+
"type": "u64"
|
|
2178
|
+
}]
|
|
2179
|
+
}
|
|
2180
|
+
];
|
|
2181
|
+
var accounts = [{
|
|
2182
|
+
"name": "ProgramState",
|
|
2183
|
+
"discriminator": [
|
|
2184
|
+
77,
|
|
2185
|
+
209,
|
|
2186
|
+
137,
|
|
2187
|
+
229,
|
|
2188
|
+
149,
|
|
2189
|
+
67,
|
|
2190
|
+
167,
|
|
2191
|
+
230
|
|
2192
|
+
]
|
|
2193
|
+
}];
|
|
2194
|
+
var events = [
|
|
2195
|
+
{
|
|
2196
|
+
"name": "DepositUpdatedEvent",
|
|
2197
|
+
"discriminator": [
|
|
2198
|
+
215,
|
|
2199
|
+
193,
|
|
2200
|
+
53,
|
|
2201
|
+
27,
|
|
2202
|
+
221,
|
|
2203
|
+
101,
|
|
2204
|
+
249,
|
|
2205
|
+
108
|
|
2206
|
+
]
|
|
2207
|
+
},
|
|
2208
|
+
{
|
|
2209
|
+
"name": "FundsWithdrawnEvent",
|
|
2210
|
+
"discriminator": [
|
|
2211
|
+
86,
|
|
2212
|
+
232,
|
|
2213
|
+
194,
|
|
2214
|
+
4,
|
|
2215
|
+
211,
|
|
2216
|
+
69,
|
|
2217
|
+
172,
|
|
2218
|
+
202
|
|
2219
|
+
]
|
|
2220
|
+
},
|
|
2221
|
+
{
|
|
2222
|
+
"name": "RespondBidirectionalEvent",
|
|
2223
|
+
"discriminator": [
|
|
2224
|
+
195,
|
|
2225
|
+
195,
|
|
2226
|
+
28,
|
|
2227
|
+
1,
|
|
2228
|
+
102,
|
|
2229
|
+
100,
|
|
2230
|
+
189,
|
|
2231
|
+
234
|
|
2232
|
+
]
|
|
2233
|
+
},
|
|
2234
|
+
{
|
|
2235
|
+
"name": "SignBidirectionalEvent",
|
|
2236
|
+
"discriminator": [
|
|
2237
|
+
135,
|
|
2238
|
+
205,
|
|
2239
|
+
217,
|
|
2240
|
+
152,
|
|
2241
|
+
96,
|
|
2242
|
+
187,
|
|
2243
|
+
11,
|
|
2244
|
+
124
|
|
2245
|
+
]
|
|
2246
|
+
},
|
|
2247
|
+
{
|
|
2248
|
+
"name": "SignatureErrorEvent",
|
|
2249
|
+
"discriminator": [
|
|
2250
|
+
42,
|
|
2251
|
+
28,
|
|
2252
|
+
210,
|
|
2253
|
+
105,
|
|
2254
|
+
9,
|
|
2255
|
+
196,
|
|
2256
|
+
189,
|
|
2257
|
+
51
|
|
2258
|
+
]
|
|
2259
|
+
},
|
|
2260
|
+
{
|
|
2261
|
+
"name": "SignatureRequestedEvent",
|
|
2262
|
+
"discriminator": [
|
|
2263
|
+
171,
|
|
2264
|
+
129,
|
|
2265
|
+
105,
|
|
2266
|
+
91,
|
|
2267
|
+
154,
|
|
2268
|
+
49,
|
|
2269
|
+
160,
|
|
2270
|
+
34
|
|
2271
|
+
]
|
|
2272
|
+
},
|
|
2273
|
+
{
|
|
2274
|
+
"name": "SignatureRespondedEvent",
|
|
2275
|
+
"discriminator": [
|
|
2276
|
+
118,
|
|
2277
|
+
146,
|
|
2278
|
+
248,
|
|
2279
|
+
151,
|
|
2280
|
+
194,
|
|
2281
|
+
93,
|
|
2282
|
+
18,
|
|
2283
|
+
86
|
|
2284
|
+
]
|
|
2285
|
+
}
|
|
2286
|
+
];
|
|
2287
|
+
var errors = [
|
|
2288
|
+
{
|
|
2289
|
+
"code": 6e3,
|
|
2290
|
+
"name": "InsufficientDeposit",
|
|
2291
|
+
"msg": "Insufficient deposit amount"
|
|
2292
|
+
},
|
|
2293
|
+
{
|
|
2294
|
+
"code": 6001,
|
|
2295
|
+
"name": "InvalidInputLength",
|
|
2296
|
+
"msg": "Arrays must have the same length"
|
|
2297
|
+
},
|
|
2298
|
+
{
|
|
2299
|
+
"code": 6002,
|
|
2300
|
+
"name": "Unauthorized",
|
|
2301
|
+
"msg": "Unauthorized access"
|
|
2302
|
+
},
|
|
2303
|
+
{
|
|
2304
|
+
"code": 6003,
|
|
2305
|
+
"name": "InsufficientFunds",
|
|
2306
|
+
"msg": "Insufficient funds for withdrawal"
|
|
2307
|
+
},
|
|
2308
|
+
{
|
|
2309
|
+
"code": 6004,
|
|
2310
|
+
"name": "InvalidRecipient",
|
|
2311
|
+
"msg": "Invalid recipient address"
|
|
2312
|
+
},
|
|
2313
|
+
{
|
|
2314
|
+
"code": 6005,
|
|
2315
|
+
"name": "InvalidTransaction",
|
|
2316
|
+
"msg": "Invalid transaction data"
|
|
2317
|
+
},
|
|
2318
|
+
{
|
|
2319
|
+
"code": 6006,
|
|
2320
|
+
"name": "MissingInstructionSysvar",
|
|
2321
|
+
"msg": "Missing instruction sysvar"
|
|
2322
|
+
}
|
|
2323
|
+
];
|
|
2324
|
+
var types = [
|
|
2325
|
+
{
|
|
2326
|
+
"name": "AffinePoint",
|
|
2327
|
+
"type": {
|
|
2328
|
+
"kind": "struct",
|
|
2329
|
+
"fields": [{
|
|
2330
|
+
"name": "x",
|
|
2331
|
+
"type": { "array": ["u8", 32] }
|
|
2332
|
+
}, {
|
|
2333
|
+
"name": "y",
|
|
2334
|
+
"type": { "array": ["u8", 32] }
|
|
2335
|
+
}]
|
|
2336
|
+
}
|
|
2337
|
+
},
|
|
2338
|
+
{
|
|
2339
|
+
"name": "DepositUpdatedEvent",
|
|
2340
|
+
"docs": ["* @dev Emitted when the deposit amount is updated.\n * @param old_deposit The previous deposit amount.\n * @param new_deposit The new deposit amount."],
|
|
2341
|
+
"type": {
|
|
2342
|
+
"kind": "struct",
|
|
2343
|
+
"fields": [{
|
|
2344
|
+
"name": "old_deposit",
|
|
2345
|
+
"type": "u64"
|
|
2346
|
+
}, {
|
|
2347
|
+
"name": "new_deposit",
|
|
2348
|
+
"type": "u64"
|
|
2349
|
+
}]
|
|
2350
|
+
}
|
|
2351
|
+
},
|
|
2352
|
+
{
|
|
2353
|
+
"name": "ErrorResponse",
|
|
2354
|
+
"type": {
|
|
2355
|
+
"kind": "struct",
|
|
2356
|
+
"fields": [{
|
|
2357
|
+
"name": "request_id",
|
|
2358
|
+
"type": { "array": ["u8", 32] }
|
|
2359
|
+
}, {
|
|
2360
|
+
"name": "error_message",
|
|
2361
|
+
"type": "string"
|
|
2362
|
+
}]
|
|
2363
|
+
}
|
|
2364
|
+
},
|
|
2365
|
+
{
|
|
2366
|
+
"name": "FundsWithdrawnEvent",
|
|
2367
|
+
"docs": ["* @dev Emitted when a withdrawal is made.\n * @param amount The amount withdrawn.\n * @param recipient The address of the recipient."],
|
|
2368
|
+
"type": {
|
|
2369
|
+
"kind": "struct",
|
|
2370
|
+
"fields": [{
|
|
2371
|
+
"name": "amount",
|
|
2372
|
+
"type": "u64"
|
|
2373
|
+
}, {
|
|
2374
|
+
"name": "recipient",
|
|
2375
|
+
"type": "pubkey"
|
|
2376
|
+
}]
|
|
2377
|
+
}
|
|
2378
|
+
},
|
|
2379
|
+
{
|
|
2380
|
+
"name": "ProgramState",
|
|
2381
|
+
"type": {
|
|
2382
|
+
"kind": "struct",
|
|
2383
|
+
"fields": [
|
|
2384
|
+
{
|
|
2385
|
+
"name": "admin",
|
|
2386
|
+
"type": "pubkey"
|
|
2387
|
+
},
|
|
2388
|
+
{
|
|
2389
|
+
"name": "signature_deposit",
|
|
2390
|
+
"type": "u64"
|
|
2391
|
+
},
|
|
2392
|
+
{
|
|
2393
|
+
"name": "chain_id",
|
|
2394
|
+
"type": "string"
|
|
2395
|
+
}
|
|
2396
|
+
]
|
|
2397
|
+
}
|
|
2398
|
+
},
|
|
2399
|
+
{
|
|
2400
|
+
"name": "RespondBidirectionalEvent",
|
|
2401
|
+
"docs": ["* @dev Emitted when a read response is received.\n * @param request_id The ID of the request. Must be calculated off-chain.\n * @param responder The address of the responder.\n * @param serialized_output The serialized output.\n * @param signature The signature."],
|
|
2402
|
+
"type": {
|
|
2403
|
+
"kind": "struct",
|
|
2404
|
+
"fields": [
|
|
2405
|
+
{
|
|
2406
|
+
"name": "request_id",
|
|
2407
|
+
"type": { "array": ["u8", 32] }
|
|
2408
|
+
},
|
|
2409
|
+
{
|
|
2410
|
+
"name": "responder",
|
|
2411
|
+
"type": "pubkey"
|
|
2412
|
+
},
|
|
2413
|
+
{
|
|
2414
|
+
"name": "serialized_output",
|
|
2415
|
+
"type": "bytes"
|
|
2416
|
+
},
|
|
2417
|
+
{
|
|
2418
|
+
"name": "signature",
|
|
2419
|
+
"type": { "defined": { "name": "Signature" } }
|
|
2420
|
+
}
|
|
2421
|
+
]
|
|
2422
|
+
}
|
|
2423
|
+
},
|
|
2424
|
+
{
|
|
2425
|
+
"name": "SignBidirectionalEvent",
|
|
2426
|
+
"docs": ["* @dev Emitted when a sign_bidirectional request is made.\n * @param sender The address of the sender.\n * @param serialized_transaction The serialized transaction to be signed.\n * @param caip2_id The SLIP-44 chain ID.\n * @param key_version The version of the key used for signing.\n * @param deposit The deposit amount.\n * @param path The derivation path for the user account.\n * @param algo The algorithm used for signing.\n * @param dest The response destination.\n * @param params Additional parameters.\n * @param program_id Program ID to callback after execution (not yet enabled).\n * @param output_deserialization_schema Schema for transaction output deserialization.\n * @param respond_serialization_schema Serialization schema for respond_bidirectional payload."],
|
|
2427
|
+
"type": {
|
|
2428
|
+
"kind": "struct",
|
|
2429
|
+
"fields": [
|
|
2430
|
+
{
|
|
2431
|
+
"name": "sender",
|
|
2432
|
+
"type": "pubkey"
|
|
2433
|
+
},
|
|
2434
|
+
{
|
|
2435
|
+
"name": "serialized_transaction",
|
|
2436
|
+
"type": "bytes"
|
|
2437
|
+
},
|
|
2438
|
+
{
|
|
2439
|
+
"name": "caip2_id",
|
|
2440
|
+
"type": "string"
|
|
2441
|
+
},
|
|
2442
|
+
{
|
|
2443
|
+
"name": "key_version",
|
|
2444
|
+
"type": "u32"
|
|
2445
|
+
},
|
|
2446
|
+
{
|
|
2447
|
+
"name": "deposit",
|
|
2448
|
+
"type": "u64"
|
|
2449
|
+
},
|
|
2450
|
+
{
|
|
2451
|
+
"name": "path",
|
|
2452
|
+
"type": "string"
|
|
2453
|
+
},
|
|
2454
|
+
{
|
|
2455
|
+
"name": "algo",
|
|
2456
|
+
"type": "string"
|
|
2457
|
+
},
|
|
2458
|
+
{
|
|
2459
|
+
"name": "dest",
|
|
2460
|
+
"type": "string"
|
|
2461
|
+
},
|
|
2462
|
+
{
|
|
2463
|
+
"name": "params",
|
|
2464
|
+
"type": "string"
|
|
2465
|
+
},
|
|
2466
|
+
{
|
|
2467
|
+
"name": "program_id",
|
|
2468
|
+
"type": "pubkey"
|
|
2469
|
+
},
|
|
2470
|
+
{
|
|
2471
|
+
"name": "output_deserialization_schema",
|
|
2472
|
+
"type": "bytes"
|
|
2473
|
+
},
|
|
2474
|
+
{
|
|
2475
|
+
"name": "respond_serialization_schema",
|
|
2476
|
+
"type": "bytes"
|
|
2477
|
+
}
|
|
2478
|
+
]
|
|
2479
|
+
}
|
|
2480
|
+
},
|
|
2481
|
+
{
|
|
2482
|
+
"name": "Signature",
|
|
2483
|
+
"type": {
|
|
2484
|
+
"kind": "struct",
|
|
2485
|
+
"fields": [
|
|
2486
|
+
{
|
|
2487
|
+
"name": "big_r",
|
|
2488
|
+
"type": { "defined": { "name": "AffinePoint" } }
|
|
2489
|
+
},
|
|
2490
|
+
{
|
|
2491
|
+
"name": "s",
|
|
2492
|
+
"type": { "array": ["u8", 32] }
|
|
2493
|
+
},
|
|
2494
|
+
{
|
|
2495
|
+
"name": "recovery_id",
|
|
2496
|
+
"type": "u8"
|
|
2497
|
+
}
|
|
2498
|
+
]
|
|
2499
|
+
}
|
|
2500
|
+
},
|
|
2501
|
+
{
|
|
2502
|
+
"name": "SignatureErrorEvent",
|
|
2503
|
+
"docs": ["* @dev Emitted when a signature error is received.\n * @notice Any address can emit this event. Do not rely on it for business logic.\n * @param request_id The ID of the request. Must be calculated off-chain.\n * @param responder The address of the responder.\n * @param error The error message."],
|
|
2504
|
+
"type": {
|
|
2505
|
+
"kind": "struct",
|
|
2506
|
+
"fields": [
|
|
2507
|
+
{
|
|
2508
|
+
"name": "request_id",
|
|
2509
|
+
"type": { "array": ["u8", 32] }
|
|
2510
|
+
},
|
|
2511
|
+
{
|
|
2512
|
+
"name": "responder",
|
|
2513
|
+
"type": "pubkey"
|
|
2514
|
+
},
|
|
2515
|
+
{
|
|
2516
|
+
"name": "error",
|
|
2517
|
+
"type": "string"
|
|
2518
|
+
}
|
|
2519
|
+
]
|
|
2520
|
+
}
|
|
2521
|
+
},
|
|
2522
|
+
{
|
|
2523
|
+
"name": "SignatureRequestedEvent",
|
|
2524
|
+
"docs": ["* @dev Emitted when a signature is requested.\n * @param sender The address of the sender.\n * @param payload The payload to be signed.\n * @param key_version The version of the key used for signing.\n * @param deposit The deposit amount.\n * @param chain_id The CAIP-2 ID of the blockchain.\n * @param path The derivation path for the user account.\n * @param algo The algorithm used for signing.\n * @param dest The response destination.\n * @param params Additional parameters.\n * @param fee_payer Optional fee payer account."],
|
|
2525
|
+
"type": {
|
|
2526
|
+
"kind": "struct",
|
|
2527
|
+
"fields": [
|
|
2528
|
+
{
|
|
2529
|
+
"name": "sender",
|
|
2530
|
+
"type": "pubkey"
|
|
2531
|
+
},
|
|
2532
|
+
{
|
|
2533
|
+
"name": "payload",
|
|
2534
|
+
"type": { "array": ["u8", 32] }
|
|
2535
|
+
},
|
|
2536
|
+
{
|
|
2537
|
+
"name": "key_version",
|
|
2538
|
+
"type": "u32"
|
|
2539
|
+
},
|
|
2540
|
+
{
|
|
2541
|
+
"name": "deposit",
|
|
2542
|
+
"type": "u64"
|
|
2543
|
+
},
|
|
2544
|
+
{
|
|
2545
|
+
"name": "chain_id",
|
|
2546
|
+
"type": "string"
|
|
2547
|
+
},
|
|
2548
|
+
{
|
|
2549
|
+
"name": "path",
|
|
2550
|
+
"type": "string"
|
|
2551
|
+
},
|
|
2552
|
+
{
|
|
2553
|
+
"name": "algo",
|
|
2554
|
+
"type": "string"
|
|
2555
|
+
},
|
|
2556
|
+
{
|
|
2557
|
+
"name": "dest",
|
|
2558
|
+
"type": "string"
|
|
2559
|
+
},
|
|
2560
|
+
{
|
|
2561
|
+
"name": "params",
|
|
2562
|
+
"type": "string"
|
|
2563
|
+
},
|
|
2564
|
+
{
|
|
2565
|
+
"name": "fee_payer",
|
|
2566
|
+
"type": { "option": "pubkey" }
|
|
2567
|
+
}
|
|
2568
|
+
]
|
|
2569
|
+
}
|
|
2570
|
+
},
|
|
2571
|
+
{
|
|
2572
|
+
"name": "SignatureRespondedEvent",
|
|
2573
|
+
"docs": ["* @dev Emitted when a signature response is received.\n * @notice Any address can emit this event. Clients should always verify the validity of the signature.\n * @param request_id The ID of the request. Must be calculated off-chain.\n * @param responder The address of the responder.\n * @param signature The signature response."],
|
|
2574
|
+
"type": {
|
|
2575
|
+
"kind": "struct",
|
|
2576
|
+
"fields": [
|
|
2577
|
+
{
|
|
2578
|
+
"name": "request_id",
|
|
2579
|
+
"type": { "array": ["u8", 32] }
|
|
2580
|
+
},
|
|
2581
|
+
{
|
|
2582
|
+
"name": "responder",
|
|
2583
|
+
"type": "pubkey"
|
|
2584
|
+
},
|
|
2585
|
+
{
|
|
2586
|
+
"name": "signature",
|
|
2587
|
+
"type": { "defined": { "name": "Signature" } }
|
|
2588
|
+
}
|
|
2589
|
+
]
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
];
|
|
2593
|
+
var chain_signatures_project_default = {
|
|
2594
|
+
address,
|
|
2595
|
+
metadata,
|
|
2596
|
+
instructions,
|
|
2597
|
+
accounts,
|
|
2598
|
+
events,
|
|
2599
|
+
errors,
|
|
2600
|
+
types
|
|
2601
|
+
};
|
|
2602
|
+
//#endregion
|
|
2603
|
+
//#region src/contracts/solana/CpiEventParser.ts
|
|
2604
|
+
const EMIT_CPI_INSTRUCTION_DISCRIMINATOR = Buffer.from([
|
|
2605
|
+
228,
|
|
2606
|
+
69,
|
|
2607
|
+
165,
|
|
2608
|
+
46,
|
|
2609
|
+
81,
|
|
2610
|
+
203,
|
|
2611
|
+
154,
|
|
2612
|
+
29
|
|
2613
|
+
]);
|
|
2614
|
+
var CpiEventParser = class {
|
|
2615
|
+
/**
|
|
2616
|
+
* Parse CPI events from an already-fetched transaction (emit_cpi! pattern).
|
|
2617
|
+
*/
|
|
2618
|
+
static parseCpiEventsFromTransaction(tx, targetProgramId, program) {
|
|
2619
|
+
const events = [];
|
|
2620
|
+
if (!tx?.meta?.innerInstructions) return events;
|
|
2621
|
+
for (const innerIxSet of tx.meta.innerInstructions) for (const instruction of innerIxSet.instructions) {
|
|
2622
|
+
if (!("programId" in instruction) || !("data" in instruction)) continue;
|
|
2623
|
+
if (instruction.programId.toString() !== targetProgramId) continue;
|
|
2624
|
+
const parsedEvent = this.parseInstruction(instruction.data, program);
|
|
2625
|
+
if (parsedEvent) events.push(parsedEvent);
|
|
2626
|
+
}
|
|
2627
|
+
return events;
|
|
2628
|
+
}
|
|
2629
|
+
/**
|
|
2630
|
+
* Fetch a transaction by signature and parse its CPI events.
|
|
2631
|
+
* Used by the subscription path where only the signature is available.
|
|
2632
|
+
*/
|
|
2633
|
+
static async fetchAndParseCpiEvents(connection, signature, targetProgramId, program) {
|
|
2634
|
+
try {
|
|
2635
|
+
const tx = await connection.getParsedTransaction(signature, {
|
|
2636
|
+
commitment: "confirmed",
|
|
2637
|
+
maxSupportedTransactionVersion: 0
|
|
2638
|
+
});
|
|
2639
|
+
return this.parseCpiEventsFromTransaction(tx, targetProgramId, program);
|
|
2640
|
+
} catch {
|
|
2641
|
+
return [];
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
/**
|
|
2645
|
+
* Parse CPI event from instruction data
|
|
2646
|
+
* Structure: [8 bytes: anchor discriminator][8 bytes: event discriminator][event data]
|
|
2647
|
+
*/
|
|
2648
|
+
static parseInstruction(instructionData, program) {
|
|
2649
|
+
try {
|
|
2650
|
+
const ixData = anchor.utils.bytes.bs58.decode(instructionData);
|
|
2651
|
+
if (ixData.length < 16) return null;
|
|
2652
|
+
const ixDiscriminator = ixData.subarray(0, 8);
|
|
2653
|
+
if (Buffer.compare(ixDiscriminator, EMIT_CPI_INSTRUCTION_DISCRIMINATOR) !== 0) return null;
|
|
2654
|
+
const eventDiscriminator = ixData.subarray(8, 16);
|
|
2655
|
+
if (!program.idl.events?.find((event) => {
|
|
2656
|
+
const idlDiscriminator = Buffer.from(event.discriminator);
|
|
2657
|
+
return Buffer.compare(eventDiscriminator, idlDiscriminator) === 0;
|
|
2658
|
+
})) return null;
|
|
2659
|
+
const fullEventData = ixData.subarray(8);
|
|
2660
|
+
return program.coder.events.decode(anchor.utils.bytes.base64.encode(fullEventData));
|
|
2661
|
+
} catch {
|
|
2662
|
+
return null;
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
/**
|
|
2666
|
+
* Subscribe to CPI events for a program
|
|
2667
|
+
*/
|
|
2668
|
+
static subscribeToCpiEvents(connection, program, eventHandlers) {
|
|
2669
|
+
return connection.onLogs(program.programId, (logs, context) => {
|
|
2670
|
+
if (logs.err) return;
|
|
2671
|
+
(async () => {
|
|
2672
|
+
const events = await this.fetchAndParseCpiEvents(connection, logs.signature, program.programId.toString(), program);
|
|
2673
|
+
for (const event of events) {
|
|
2674
|
+
const handler = eventHandlers.get(event.name);
|
|
2675
|
+
if (handler) try {
|
|
2676
|
+
await handler(event.data, context.slot);
|
|
2677
|
+
} catch {}
|
|
2678
|
+
}
|
|
2679
|
+
})();
|
|
2680
|
+
}, "confirmed");
|
|
2681
|
+
}
|
|
2682
|
+
};
|
|
2683
|
+
//#endregion
|
|
2684
|
+
//#region src/contracts/solana/utils.ts
|
|
2685
|
+
/**
|
|
2686
|
+
* Generates a unique request ID for Solana signature requests using keccak256 hashing.
|
|
2687
|
+
*
|
|
2688
|
+
* The request ID is computed by ABI-encoding the request parameters and hashing
|
|
2689
|
+
* the result. This ID is used to track signature requests and match them with
|
|
2690
|
+
* responses from the MPC network.
|
|
2691
|
+
*
|
|
2692
|
+
* @param request - The signature request parameters
|
|
2693
|
+
* @param request.address - The sender's address (Solana public key as string)
|
|
2694
|
+
* @param request.payload - The data payload to be signed
|
|
2695
|
+
* @param request.path - The derivation path for the signing key
|
|
2696
|
+
* @param request.keyVersion - The version of the signing key
|
|
2697
|
+
* @param request.chainId - The CAIP-2 chain identifier (e.g., 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp')
|
|
2698
|
+
* @param request.algo - The signing algorithm identifier
|
|
2699
|
+
* @param request.dest - The destination identifier for the signature
|
|
2700
|
+
* @param request.params - Additional parameters for the signing process
|
|
2701
|
+
* @returns The keccak256 hash of the encoded request as a hex string
|
|
2702
|
+
*
|
|
2703
|
+
* @example
|
|
2704
|
+
* ```typescript
|
|
2705
|
+
* const requestId = getRequestIdRespond({
|
|
2706
|
+
* address: 'So11111111111111111111111111111111111111112',
|
|
2707
|
+
* payload: new Uint8Array([1, 2, 3, 4]),
|
|
2708
|
+
* path: 'solana,1',
|
|
2709
|
+
* keyVersion: 0,
|
|
2710
|
+
* chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
2711
|
+
* algo: '',
|
|
2712
|
+
* dest: '',
|
|
2713
|
+
* params: '',
|
|
2714
|
+
* })
|
|
2715
|
+
* ```
|
|
2716
|
+
*/
|
|
2717
|
+
function getRequestIdRespond(request) {
|
|
2718
|
+
const payloadHex = bytesToHex(new Uint8Array(request.payload));
|
|
2719
|
+
return keccak256(encodeAbiParameters(parseAbiParameters("string, bytes, string, uint32, string, string, string, string"), [
|
|
2720
|
+
request.address,
|
|
2721
|
+
payloadHex,
|
|
2722
|
+
request.path,
|
|
2723
|
+
request.keyVersion,
|
|
2724
|
+
request.chainId,
|
|
2725
|
+
request.algo,
|
|
2726
|
+
request.dest,
|
|
2727
|
+
request.params
|
|
2728
|
+
]));
|
|
2729
|
+
}
|
|
2730
|
+
/**
|
|
2731
|
+
* Generates a unique request ID for bidirectional sign operations using keccak256 hashing.
|
|
2732
|
+
*
|
|
2733
|
+
* Unlike `getRequestIdRespond`, this function uses packed encoding (solidityPacked)
|
|
2734
|
+
* instead of standard ABI encoding. This is used for cross-chain bidirectional
|
|
2735
|
+
* signing flows where the request originates from a different chain.
|
|
2736
|
+
*
|
|
2737
|
+
* @param request - The bidirectional signature request parameters
|
|
2738
|
+
* @param request.sender - The sender's address (Solana public key as string)
|
|
2739
|
+
* @param request.payload - The data payload to be signed
|
|
2740
|
+
* @param request.caip2Id - The CAIP-2 chain identifier (e.g., 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp')
|
|
2741
|
+
* @param request.keyVersion - The version of the signing key
|
|
2742
|
+
* @param request.path - The derivation path for the signing key
|
|
2743
|
+
* @param request.algo - The signing algorithm identifier
|
|
2744
|
+
* @param request.dest - The destination identifier for the signature
|
|
2745
|
+
* @param request.params - Additional parameters for the signing process
|
|
2746
|
+
* @returns The keccak256 hash of the packed encoded request as a hex string
|
|
2747
|
+
*
|
|
2748
|
+
* @example
|
|
2749
|
+
* ```typescript
|
|
2750
|
+
* const requestId = getRequestIdBidirectional({
|
|
2751
|
+
* sender: 'So11111111111111111111111111111111111111112',
|
|
2752
|
+
* payload: new Uint8Array([1, 2, 3, 4]),
|
|
2753
|
+
* caip2Id: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
2754
|
+
* keyVersion: 0,
|
|
2755
|
+
* path: 'ethereum,1',
|
|
2756
|
+
* algo: '',
|
|
2757
|
+
* dest: '',
|
|
2758
|
+
* params: '',
|
|
2759
|
+
* })
|
|
2760
|
+
* ```
|
|
2761
|
+
*/
|
|
2762
|
+
function getRequestIdBidirectional(request) {
|
|
2763
|
+
const payloadHex = bytesToHex(new Uint8Array(request.payload));
|
|
2764
|
+
return keccak256(encodePacked([
|
|
2765
|
+
"string",
|
|
2766
|
+
"bytes",
|
|
2767
|
+
"string",
|
|
2768
|
+
"uint32",
|
|
2769
|
+
"string",
|
|
2770
|
+
"string",
|
|
2771
|
+
"string",
|
|
2772
|
+
"string"
|
|
2773
|
+
], [
|
|
2774
|
+
request.sender,
|
|
2775
|
+
payloadHex,
|
|
2776
|
+
request.caip2Id,
|
|
2777
|
+
request.keyVersion,
|
|
2778
|
+
request.path,
|
|
2779
|
+
request.algo,
|
|
2780
|
+
request.dest,
|
|
2781
|
+
request.params
|
|
2782
|
+
]));
|
|
2783
|
+
}
|
|
2784
|
+
//#endregion
|
|
2785
|
+
//#region src/contracts/solana/ChainSignaturesContract.ts
|
|
2786
|
+
var ChainSignatureContract = class extends ChainSignatureContract$2 {
|
|
2787
|
+
provider;
|
|
2788
|
+
program;
|
|
2789
|
+
programId;
|
|
2790
|
+
rootPublicKey;
|
|
2791
|
+
requesterAddress;
|
|
2792
|
+
_connection;
|
|
2793
|
+
/**
|
|
2794
|
+
* Creates a new instance of the ChainSignatureContract for Solana chains.
|
|
2795
|
+
*
|
|
2796
|
+
* @param args - Configuration options for the contract
|
|
2797
|
+
* @param args.provider - An Anchor Provider for interacting with Solana
|
|
2798
|
+
* @param args.programId - The program ID as a string or PublicKey
|
|
2799
|
+
* @param args.config - Optional configuration
|
|
2800
|
+
* @param args.config.rootPublicKey - Optional root public key. If not provided, it will be derived from the program ID
|
|
2801
|
+
* @param args.config.requesterAddress - Provider wallet address is always the fee payer but requester can be overridden
|
|
2802
|
+
* @param args.config.idl - Optional custom IDL. If not provided, the default ChainSignatures IDL will be used
|
|
2803
|
+
* @param args.config.disableRetryOnRateLimit - If true, disables @solana/web3.js automatic retry on 429 responses. Recommended when using the built-in backfill mechanism.
|
|
2804
|
+
*/
|
|
2805
|
+
constructor(args) {
|
|
2806
|
+
super();
|
|
2807
|
+
this.provider = args.provider;
|
|
2808
|
+
this.requesterAddress = args.config?.requesterAddress ?? this.provider.wallet.publicKey.toString();
|
|
2809
|
+
this.programId = typeof args.programId === "string" ? new PublicKey(args.programId) : args.programId;
|
|
2810
|
+
const idl = args.config?.idl || chain_signatures_project_default;
|
|
2811
|
+
this.program = new Program({
|
|
2812
|
+
...idl,
|
|
2813
|
+
address: this.programId.toString()
|
|
2814
|
+
}, this.provider);
|
|
2815
|
+
const rootPublicKey = args.config?.rootPublicKey || getRootPublicKey(this.programId.toString(), CHAINS.SOLANA);
|
|
2816
|
+
if (!rootPublicKey) throw new Error(`Invalid public key, please provide a valid root public key or program ID`);
|
|
2817
|
+
this.rootPublicKey = normalizeToUncompressedPubKey(rootPublicKey);
|
|
2818
|
+
if (args.config?.disableRetryOnRateLimit !== void 0) this._connection = new Connection(this.provider.connection.rpcEndpoint, {
|
|
2819
|
+
commitment: this.provider.connection.commitment,
|
|
2820
|
+
disableRetryOnRateLimit: args.config.disableRetryOnRateLimit,
|
|
2821
|
+
fetch: async (input, init) => {
|
|
2822
|
+
const res = await globalThis.fetch(input, init);
|
|
2823
|
+
if (res.status === 429) {
|
|
2824
|
+
let method = "unknown";
|
|
2825
|
+
try {
|
|
2826
|
+
const body = JSON.parse(init?.body);
|
|
2827
|
+
method = Array.isArray(body) ? body.map((r) => r.method).join(", ") : body.method ?? "unknown";
|
|
2828
|
+
} catch {}
|
|
2829
|
+
console.warn(`\n[429 TRACE] RPC method: ${method}\n${(/* @__PURE__ */ new Error()).stack}`);
|
|
2830
|
+
}
|
|
2831
|
+
return res;
|
|
2832
|
+
}
|
|
2833
|
+
});
|
|
2834
|
+
else this._connection = this.provider.connection;
|
|
2835
|
+
}
|
|
2836
|
+
/**
|
|
2837
|
+
* Gets the connection, using the override if `disableRetryOnRateLimit` was configured.
|
|
2838
|
+
*/
|
|
2839
|
+
get connection() {
|
|
2840
|
+
return this._connection;
|
|
2841
|
+
}
|
|
2842
|
+
async getCurrentSignatureDeposit() {
|
|
2843
|
+
try {
|
|
2844
|
+
const programStatePDA = await this.getProgramStatePDA();
|
|
2845
|
+
const programState = await this.program.account.programState.fetch(programStatePDA);
|
|
2846
|
+
return BigInt(programState.signatureDeposit.toString());
|
|
2847
|
+
} catch (error) {
|
|
2848
|
+
throw new Error(`Failed to get signature deposit: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
/**
|
|
2852
|
+
* Get the Program State PDA
|
|
2853
|
+
*/
|
|
2854
|
+
async getProgramStatePDA() {
|
|
2855
|
+
const [pda] = PublicKey.findProgramAddressSync([new TextEncoder().encode("program-state")], this.programId);
|
|
2856
|
+
return pda;
|
|
2857
|
+
}
|
|
2858
|
+
async getDerivedPublicKey(args) {
|
|
2859
|
+
return deriveChildPublicKey(await this.getPublicKey(), args.predecessor, args.path, KDF_CHAIN_IDS.SOLANA, args.keyVersion);
|
|
2860
|
+
}
|
|
2861
|
+
async getPublicKey() {
|
|
2862
|
+
return this.rootPublicKey;
|
|
2863
|
+
}
|
|
2864
|
+
async getSignRequestInstruction(args, options) {
|
|
2865
|
+
const fixedRemainingAccounts = [{
|
|
2866
|
+
pubkey: PublicKey.findProgramAddressSync([new TextEncoder().encode("__event_authority")], this.program.programId)[0],
|
|
2867
|
+
isWritable: false,
|
|
2868
|
+
isSigner: false
|
|
2869
|
+
}, {
|
|
2870
|
+
pubkey: this.program.programId,
|
|
2871
|
+
isWritable: false,
|
|
2872
|
+
isSigner: false
|
|
2873
|
+
}];
|
|
2874
|
+
return await this.program.methods.sign(Array.from(args.payload), args.key_version, args.path, options?.sign?.algo || "", options?.sign?.dest || "", options?.sign?.params || "").accounts({
|
|
2875
|
+
requester: this.requesterAddress,
|
|
2876
|
+
feePayer: this.provider.wallet.publicKey,
|
|
2877
|
+
program: this.programId
|
|
2878
|
+
}).remainingAccounts([...fixedRemainingAccounts, ...options?.remainingAccounts ?? []]).instruction();
|
|
2879
|
+
}
|
|
2880
|
+
/**
|
|
2881
|
+
* Sends a transaction to the program to request a signature, then
|
|
2882
|
+
* races a WebSocket listener against polling backfill to find the result.
|
|
2883
|
+
* If the signature is not found within the timeout, it will throw an error.
|
|
2884
|
+
*/
|
|
2885
|
+
async sign(args, options) {
|
|
2886
|
+
const algo = options?.sign?.algo ?? "";
|
|
2887
|
+
const dest = options?.sign?.dest ?? "";
|
|
2888
|
+
const params = options?.sign?.params ?? "";
|
|
2889
|
+
const delay = options?.retry?.delay ?? 5e3;
|
|
2890
|
+
const timeoutMs = delay * (options?.retry?.retryCount ?? 12);
|
|
2891
|
+
if (options?.remainingAccounts?.filter((acc) => acc.isSigner)?.some((acc) => !options?.remainingSigners?.some((signer) => signer.publicKey.equals(acc.pubkey)))) throw new Error("All accounts marked as signers must have a corresponding signer");
|
|
2892
|
+
const requestId = this.getRequestId(args, {
|
|
2893
|
+
algo,
|
|
2894
|
+
dest,
|
|
2895
|
+
params
|
|
2896
|
+
});
|
|
2897
|
+
const instruction = await this.getSignRequestInstruction(args, {
|
|
2898
|
+
sign: {
|
|
2899
|
+
algo,
|
|
2900
|
+
dest,
|
|
2901
|
+
params
|
|
2902
|
+
},
|
|
2903
|
+
remainingAccounts: options?.remainingAccounts
|
|
2904
|
+
});
|
|
2905
|
+
const transaction = new Transaction().add(instruction);
|
|
2906
|
+
transaction.feePayer = this.provider.wallet.publicKey;
|
|
2907
|
+
const hash = await this.sendAndConfirmWithoutWebSocket(transaction, options?.remainingSigners);
|
|
2908
|
+
try {
|
|
2909
|
+
const result = await this.waitForEvent({
|
|
2910
|
+
eventName: "signatureRespondedEvent",
|
|
2911
|
+
requestId,
|
|
2912
|
+
signer: this.programId,
|
|
2913
|
+
afterSignature: hash,
|
|
2914
|
+
timeoutMs,
|
|
2915
|
+
backfillIntervalMs: delay
|
|
2916
|
+
});
|
|
2917
|
+
if (!await verifyRecoveredAddress(result, args.payload, this.requesterAddress, args.path, this, args.key_version)) throw new SigningError(requestId, { hash }, /* @__PURE__ */ new Error("Signature verification failed: recovered address does not match expected address"));
|
|
2918
|
+
return result;
|
|
2919
|
+
} catch (error) {
|
|
2920
|
+
if (error instanceof SignatureNotFoundError) throw error;
|
|
2921
|
+
else throw new SigningError(requestId, { hash }, error instanceof Error ? error : void 0);
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
async sendAndConfirmWithoutWebSocket(transaction, signers) {
|
|
2925
|
+
const { blockhash } = await this.connection.getLatestBlockhash("confirmed");
|
|
2926
|
+
transaction.recentBlockhash = blockhash;
|
|
2927
|
+
transaction = await this.provider.wallet.signTransaction(transaction);
|
|
2928
|
+
if (signers && signers.length > 0) transaction.partialSign(...signers);
|
|
2929
|
+
const signature = await this.connection.sendRawTransaction(transaction.serialize(), {
|
|
2930
|
+
skipPreflight: false,
|
|
2931
|
+
preflightCommitment: "processed",
|
|
2932
|
+
maxRetries: 3
|
|
2933
|
+
});
|
|
2934
|
+
const startTime = Date.now();
|
|
2935
|
+
const timeout = 3e4;
|
|
2936
|
+
while (Date.now() - startTime < timeout) {
|
|
2937
|
+
const status = await this.connection.getSignatureStatus(signature);
|
|
2938
|
+
if (status.value?.err) throw new Error(`Transaction failed: ${JSON.stringify(status.value.err)}`);
|
|
2939
|
+
if (status.value?.confirmationStatus === "confirmed" || status.value?.confirmationStatus === "finalized") return signature;
|
|
2940
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
2941
|
+
}
|
|
2942
|
+
throw new TransactionExpiredTimeoutError(signature, timeout / 1e3);
|
|
2943
|
+
}
|
|
2944
|
+
/**
|
|
2945
|
+
* Waits for a specific event matching the given requestId by combining
|
|
2946
|
+
* a WebSocket listener (real-time) with polling backfill (resilience).
|
|
2947
|
+
*/
|
|
2948
|
+
async waitForEvent(options) {
|
|
2949
|
+
const { eventName, requestId, signer, afterSignature, timeoutMs = 6e4, backfillIntervalMs = 3e4, backfillLimit = 50, healthCheckIntervalMs = 1e4, signal } = options;
|
|
2950
|
+
return await new Promise((resolve, reject) => {
|
|
2951
|
+
let settled = false;
|
|
2952
|
+
const seenSignatures = /* @__PURE__ */ new Set();
|
|
2953
|
+
let lastCheckedSignature = afterSignature;
|
|
2954
|
+
const cleanupFns = [];
|
|
2955
|
+
const cleanup = () => {
|
|
2956
|
+
for (const fn of cleanupFns) try {
|
|
2957
|
+
fn();
|
|
2958
|
+
} catch {}
|
|
2959
|
+
};
|
|
2960
|
+
const settle = (action) => {
|
|
2961
|
+
if (settled) return;
|
|
2962
|
+
settled = true;
|
|
2963
|
+
cleanup();
|
|
2964
|
+
action();
|
|
2965
|
+
};
|
|
2966
|
+
const processEvent = (name, data, txSignature) => {
|
|
2967
|
+
if (settled) return false;
|
|
2968
|
+
if (txSignature && seenSignatures.has(txSignature)) return false;
|
|
2969
|
+
if (txSignature) seenSignatures.add(txSignature);
|
|
2970
|
+
if (name !== eventName) return false;
|
|
2971
|
+
const result = this.mapEventForName(eventName, data, requestId);
|
|
2972
|
+
if (result !== void 0) {
|
|
2973
|
+
settle(() => {
|
|
2974
|
+
resolve(result);
|
|
2975
|
+
});
|
|
2976
|
+
return true;
|
|
2977
|
+
}
|
|
2978
|
+
return false;
|
|
2979
|
+
};
|
|
2980
|
+
if (signal) {
|
|
2981
|
+
if (signal.aborted) {
|
|
2982
|
+
settle(() => {
|
|
2983
|
+
reject(signal.reason ?? /* @__PURE__ */ new Error("Aborted"));
|
|
2984
|
+
});
|
|
2985
|
+
return;
|
|
2986
|
+
}
|
|
2987
|
+
const onAbort = () => {
|
|
2988
|
+
settle(() => {
|
|
2989
|
+
reject(signal.reason ?? /* @__PURE__ */ new Error("Aborted"));
|
|
2990
|
+
});
|
|
2991
|
+
};
|
|
2992
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2993
|
+
cleanupFns.push(() => {
|
|
2994
|
+
signal.removeEventListener("abort", onAbort);
|
|
2995
|
+
});
|
|
2996
|
+
}
|
|
2997
|
+
const timeoutId = setTimeout(() => {
|
|
2998
|
+
settle(() => {
|
|
2999
|
+
reject(new SignatureNotFoundError(requestId));
|
|
3000
|
+
});
|
|
3001
|
+
}, timeoutMs);
|
|
3002
|
+
cleanupFns.push(() => {
|
|
3003
|
+
clearTimeout(timeoutId);
|
|
3004
|
+
});
|
|
3005
|
+
const runBackfill = async () => {
|
|
3006
|
+
if (settled) return;
|
|
3007
|
+
const parser = new EventParser(this.program.programId, this.program.coder);
|
|
3008
|
+
try {
|
|
3009
|
+
const signatures = await this.connection.getSignaturesForAddress(signer, {
|
|
3010
|
+
until: lastCheckedSignature,
|
|
3011
|
+
limit: backfillLimit
|
|
3012
|
+
}, "confirmed");
|
|
3013
|
+
if (signatures.length > 0) lastCheckedSignature = signatures[0].signature;
|
|
3014
|
+
for (const sig of signatures) {
|
|
3015
|
+
if (settled) return;
|
|
3016
|
+
if (seenSignatures.has(sig.signature)) continue;
|
|
3017
|
+
const tx = await this.connection.getParsedTransaction(sig.signature, {
|
|
3018
|
+
commitment: "confirmed",
|
|
3019
|
+
maxSupportedTransactionVersion: 0
|
|
3020
|
+
});
|
|
3021
|
+
if (!tx) continue;
|
|
3022
|
+
const cpiEvents = CpiEventParser.parseCpiEventsFromTransaction(tx, this.programId.toString(), this.program);
|
|
3023
|
+
for (const event of cpiEvents) if (processEvent(event.name, event.data, sig.signature)) return;
|
|
3024
|
+
const logs = tx.meta?.logMessages;
|
|
3025
|
+
if (logs) for (const evt of parser.parseLogs(logs)) {
|
|
3026
|
+
if (!evt) continue;
|
|
3027
|
+
if (processEvent(evt.name, evt.data, sig.signature)) return;
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
} catch {}
|
|
3031
|
+
};
|
|
3032
|
+
let lastWsCallbackTime = Date.now();
|
|
3033
|
+
let currentSubId;
|
|
3034
|
+
const subscribeToLogs = () => {
|
|
3035
|
+
const parser = new EventParser(this.program.programId, this.program.coder);
|
|
3036
|
+
currentSubId = this.connection.onLogs(signer, (logs, _context) => {
|
|
3037
|
+
if (settled) return;
|
|
3038
|
+
lastWsCallbackTime = Date.now();
|
|
3039
|
+
if (logs.err) return;
|
|
3040
|
+
for (const evt of parser.parseLogs(logs.logs)) {
|
|
3041
|
+
if (!evt) continue;
|
|
3042
|
+
if (processEvent(evt.name, evt.data, logs.signature)) return;
|
|
3043
|
+
}
|
|
3044
|
+
CpiEventParser.fetchAndParseCpiEvents(this.connection, logs.signature, this.programId.toString(), this.program).then((cpiEvents) => {
|
|
3045
|
+
for (const event of cpiEvents) if (processEvent(event.name, event.data, logs.signature)) return;
|
|
3046
|
+
});
|
|
3047
|
+
}, "confirmed");
|
|
3048
|
+
cleanupFns.push(() => {
|
|
3049
|
+
if (currentSubId !== void 0) this.connection.removeOnLogsListener(currentSubId);
|
|
3050
|
+
});
|
|
3051
|
+
};
|
|
3052
|
+
subscribeToLogs();
|
|
3053
|
+
let fastBackfillId;
|
|
3054
|
+
const startFastBackfill = () => {
|
|
3055
|
+
if (fastBackfillId !== void 0) return;
|
|
3056
|
+
fastBackfillId = setInterval(() => {
|
|
3057
|
+
runBackfill();
|
|
3058
|
+
}, 5e3);
|
|
3059
|
+
cleanupFns.push(() => {
|
|
3060
|
+
if (fastBackfillId !== void 0) {
|
|
3061
|
+
clearInterval(fastBackfillId);
|
|
3062
|
+
fastBackfillId = void 0;
|
|
3063
|
+
}
|
|
3064
|
+
});
|
|
3065
|
+
};
|
|
3066
|
+
const stopFastBackfill = () => {
|
|
3067
|
+
if (fastBackfillId !== void 0) {
|
|
3068
|
+
clearInterval(fastBackfillId);
|
|
3069
|
+
fastBackfillId = void 0;
|
|
3070
|
+
}
|
|
3071
|
+
};
|
|
3072
|
+
const healthCheckId = setInterval(() => {
|
|
3073
|
+
if (settled) return;
|
|
3074
|
+
this.connection.getSlot("confirmed").then(() => {
|
|
3075
|
+
if (Date.now() - lastWsCallbackTime < healthCheckIntervalMs * 3) stopFastBackfill();
|
|
3076
|
+
}).catch(() => {
|
|
3077
|
+
if (currentSubId !== void 0) {
|
|
3078
|
+
this.connection.removeOnLogsListener(currentSubId);
|
|
3079
|
+
currentSubId = void 0;
|
|
3080
|
+
}
|
|
3081
|
+
subscribeToLogs();
|
|
3082
|
+
startFastBackfill();
|
|
3083
|
+
});
|
|
3084
|
+
}, healthCheckIntervalMs);
|
|
3085
|
+
cleanupFns.push(() => {
|
|
3086
|
+
clearInterval(healthCheckId);
|
|
3087
|
+
});
|
|
3088
|
+
const safetyBackfillId = setInterval(() => {
|
|
3089
|
+
runBackfill();
|
|
3090
|
+
}, backfillIntervalMs);
|
|
3091
|
+
cleanupFns.push(() => {
|
|
3092
|
+
clearInterval(safetyBackfillId);
|
|
3093
|
+
});
|
|
3094
|
+
});
|
|
3095
|
+
}
|
|
3096
|
+
mapRespondToResult(data, requestId) {
|
|
3097
|
+
if ("0x" + hex.encode(new Uint8Array(data.requestId)) !== requestId) return void 0;
|
|
3098
|
+
return {
|
|
3099
|
+
r: hex.encode(new Uint8Array(data.signature.bigR.x)),
|
|
3100
|
+
s: hex.encode(new Uint8Array(data.signature.s)),
|
|
3101
|
+
v: data.signature.recoveryId + 27
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
mapRespondErrorToResult(data, requestId) {
|
|
3105
|
+
const eventRequestIdHex = "0x" + hex.encode(new Uint8Array(data.requestId));
|
|
3106
|
+
if (eventRequestIdHex !== requestId) return void 0;
|
|
3107
|
+
return {
|
|
3108
|
+
requestId: eventRequestIdHex,
|
|
3109
|
+
error: data.error
|
|
3110
|
+
};
|
|
3111
|
+
}
|
|
3112
|
+
mapRespondBidirectionalToResult(data, requestId) {
|
|
3113
|
+
if ("0x" + hex.encode(new Uint8Array(data.requestId)) !== requestId) return void 0;
|
|
3114
|
+
return {
|
|
3115
|
+
serializedOutput: data.serializedOutput,
|
|
3116
|
+
signature: data.signature
|
|
3117
|
+
};
|
|
3118
|
+
}
|
|
3119
|
+
mapEventForName(eventName, data, requestId) {
|
|
3120
|
+
switch (eventName) {
|
|
3121
|
+
case "signatureRespondedEvent": return this.mapRespondToResult(data, requestId);
|
|
3122
|
+
case "signatureErrorEvent": return this.mapRespondErrorToResult(data, requestId);
|
|
3123
|
+
case "respondBidirectionalEvent": return this.mapRespondBidirectionalToResult(data, requestId);
|
|
3124
|
+
default: return;
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
/**
|
|
3128
|
+
* Generates the request ID for a signature request allowing to track the response.
|
|
3129
|
+
*/
|
|
3130
|
+
getRequestId(args, options = {
|
|
3131
|
+
algo: "",
|
|
3132
|
+
dest: "",
|
|
3133
|
+
params: ""
|
|
3134
|
+
}) {
|
|
3135
|
+
return getRequestIdRespond({
|
|
3136
|
+
payload: args.payload,
|
|
3137
|
+
path: args.path,
|
|
3138
|
+
keyVersion: args.key_version,
|
|
3139
|
+
algo: options.algo || "",
|
|
3140
|
+
dest: options.dest || "",
|
|
3141
|
+
params: options.params || "",
|
|
3142
|
+
address: this.requesterAddress,
|
|
3143
|
+
chainId: KDF_CHAIN_IDS.SOLANA
|
|
3144
|
+
});
|
|
3145
|
+
}
|
|
3146
|
+
};
|
|
3147
|
+
//#endregion
|
|
3148
|
+
//#region src/contracts/solana/index.ts
|
|
3149
|
+
var solana_exports = /* @__PURE__ */ __exportAll({
|
|
3150
|
+
ChainSignatureContract: () => ChainSignatureContract,
|
|
3151
|
+
getRequestIdBidirectional: () => getRequestIdBidirectional,
|
|
3152
|
+
getRequestIdRespond: () => getRequestIdRespond,
|
|
3153
|
+
utils: () => utils
|
|
3154
|
+
});
|
|
3155
|
+
const utils = {
|
|
3156
|
+
ChainSignaturesContractIdl: chain_signatures_project_exports,
|
|
3157
|
+
errors: errors_exports
|
|
3158
|
+
};
|
|
3159
|
+
//#endregion
|
|
3160
|
+
//#region src/contracts/index.ts
|
|
3161
|
+
var contracts_exports = /* @__PURE__ */ __exportAll({
|
|
3162
|
+
ChainSignatureContract: () => ChainSignatureContract$2,
|
|
3163
|
+
evm: () => evm_exports,
|
|
3164
|
+
solana: () => solana_exports
|
|
3165
|
+
});
|
|
3166
|
+
//#endregion
|
|
3167
|
+
export { chain_adapters_exports as chainAdapters, constants_exports as constants, contracts_exports as contracts, utils_exports as utils };
|