signet.js 0.0.1-beta.0
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/README.md +75 -0
- package/package.json +64 -0
- package/src/chains/Bitcoin/BTCRpcAdapter/BTCRpcAdapter.d.ts +10 -0
- package/src/chains/Bitcoin/BTCRpcAdapter/BTCRpcAdapter.js +2 -0
- package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/Mempool.d.ts +15 -0
- package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/Mempool.js +69 -0
- package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/index.d.ts +1 -0
- package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/index.js +1 -0
- package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/types.d.ts +69 -0
- package/src/chains/Bitcoin/BTCRpcAdapter/Mempool/types.js +1 -0
- package/src/chains/Bitcoin/BTCRpcAdapter/index.d.ts +5 -0
- package/src/chains/Bitcoin/BTCRpcAdapter/index.js +5 -0
- package/src/chains/Bitcoin/Bitcoin.d.ts +66 -0
- package/src/chains/Bitcoin/Bitcoin.js +197 -0
- package/src/chains/Bitcoin/types.d.ts +42 -0
- package/src/chains/Bitcoin/types.js +1 -0
- package/src/chains/Bitcoin/utils.d.ts +2 -0
- package/src/chains/Bitcoin/utils.js +13 -0
- package/src/chains/Chain.d.ts +89 -0
- package/src/chains/Chain.js +9 -0
- package/src/chains/ChainSignatureContract.d.ts +62 -0
- package/src/chains/ChainSignatureContract.js +7 -0
- package/src/chains/Cosmos/Cosmos.d.ts +49 -0
- package/src/chains/Cosmos/Cosmos.js +156 -0
- package/src/chains/Cosmos/types.d.ts +30 -0
- package/src/chains/Cosmos/types.js +1 -0
- package/src/chains/Cosmos/utils.d.ts +2 -0
- package/src/chains/Cosmos/utils.js +27 -0
- package/src/chains/EVM/EVM.d.ts +40 -0
- package/src/chains/EVM/EVM.js +108 -0
- package/src/chains/EVM/types.d.ts +5 -0
- package/src/chains/EVM/types.js +1 -0
- package/src/chains/EVM/utils.d.ts +7 -0
- package/src/chains/EVM/utils.js +14 -0
- package/src/chains/index.d.ts +12 -0
- package/src/chains/index.js +12 -0
- package/src/chains/types.d.ts +31 -0
- package/src/chains/types.js +1 -0
- package/src/chains/utils.d.ts +12 -0
- package/src/chains/utils.js +27 -0
- package/src/index.d.ts +2 -0
- package/src/index.js +2 -0
- package/src/utils/chains/index.d.ts +1 -0
- package/src/utils/chains/index.js +1 -0
- package/src/utils/chains/near/account.d.ts +13 -0
- package/src/utils/chains/near/account.js +22 -0
- package/src/utils/chains/near/constants.d.ts +3 -0
- package/src/utils/chains/near/constants.js +3 -0
- package/src/utils/chains/near/contract.d.ts +39 -0
- package/src/utils/chains/near/contract.js +101 -0
- package/src/utils/chains/near/index.d.ts +3 -0
- package/src/utils/chains/near/index.js +3 -0
- package/src/utils/chains/near/relayer/index.d.ts +1 -0
- package/src/utils/chains/near/relayer/index.js +1 -0
- package/src/utils/chains/near/relayer/relayer.d.ts +8 -0
- package/src/utils/chains/near/relayer/relayer.js +33 -0
- package/src/utils/chains/near/relayer/types.d.ts +22 -0
- package/src/utils/chains/near/relayer/types.js +1 -0
- package/src/utils/chains/near/signAndSend/index.d.ts +1 -0
- package/src/utils/chains/near/signAndSend/index.js +1 -0
- package/src/utils/chains/near/signAndSend/keypair.d.ts +6 -0
- package/src/utils/chains/near/signAndSend/keypair.js +126 -0
- package/src/utils/chains/near/transactionBuilder.d.ts +26 -0
- package/src/utils/chains/near/transactionBuilder.js +72 -0
- package/src/utils/chains/near/types.d.ts +47 -0
- package/src/utils/chains/near/types.js +1 -0
- package/src/utils/index.d.ts +1 -0
- package/src/utils/index.js +1 -0
- package/vocs.config.d.ts +3 -0
- package/vocs.config.js +71 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { KeyDerivationPath, MPCPayloads, RSVSignature } from '@chains';
|
|
2
|
+
/**
|
|
3
|
+
* Core interface for blockchain implementations.
|
|
4
|
+
* Provides a standardized way to interact with different blockchain networks through a common set of methods.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam TransactionRequest - The type of transaction request specific to the blockchain
|
|
7
|
+
* @typeParam UnsignedTransaction - The type of unsigned transaction specific to the blockchain
|
|
8
|
+
*/
|
|
9
|
+
export declare abstract class Chain<TransactionRequest, UnsignedTransaction> {
|
|
10
|
+
/**
|
|
11
|
+
* Gets the native token balance for a given address
|
|
12
|
+
*
|
|
13
|
+
* @param address - The blockchain address to check
|
|
14
|
+
* @returns Promise resolving to the balance as a string, formatted according to the chain's decimal places (e.g. ETH, BTC, etc.)
|
|
15
|
+
* @throws Error if the balance fetch fails or the address is invalid
|
|
16
|
+
*/
|
|
17
|
+
abstract getBalance(address: string): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Derives an address and public key from a signer ID and derivation path.
|
|
20
|
+
* Uses MPC (Multi-Party Computation) to derive the key pair securely.
|
|
21
|
+
*
|
|
22
|
+
* @param predecessor - The signer ID used to call the sign function on ChainSignatureContract
|
|
23
|
+
* @param path - The derivation path that uniquely identifies this key pair
|
|
24
|
+
* @returns Promise resolving to the derived address and its corresponding public key
|
|
25
|
+
* @throws Error if key derivation fails or the signer ID is invalid
|
|
26
|
+
*/
|
|
27
|
+
abstract deriveAddressAndPublicKey(predecessor: string, path: KeyDerivationPath): Promise<{
|
|
28
|
+
address: string;
|
|
29
|
+
publicKey: string;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Stores an unsigned transaction in local storage for later use.
|
|
33
|
+
* This method persists transaction data between page reloads and browser sessions.
|
|
34
|
+
* Particularly useful for browser-based wallets that need to maintain transaction state.
|
|
35
|
+
*
|
|
36
|
+
* @param transaction - The unsigned transaction to store
|
|
37
|
+
* @param storageKey - Unique key to identify the stored transaction
|
|
38
|
+
*/
|
|
39
|
+
abstract setTransaction(transaction: UnsignedTransaction, storageKey: string): void;
|
|
40
|
+
/**
|
|
41
|
+
* Retrieves a previously stored transaction from local storage.
|
|
42
|
+
*
|
|
43
|
+
* @param storageKey - The key used to store the transaction
|
|
44
|
+
* @param options - Additional options
|
|
45
|
+
* @param options.remove - If true, removes the transaction from storage after retrieval
|
|
46
|
+
* @returns The stored transaction or undefined if not found
|
|
47
|
+
*/
|
|
48
|
+
abstract getTransaction(storageKey: string, options?: {
|
|
49
|
+
remove?: boolean;
|
|
50
|
+
}): UnsignedTransaction | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Prepares a transaction for MPC signing by creating the necessary payloads.
|
|
53
|
+
* This method handles chain-specific transaction preparation including:
|
|
54
|
+
* - Fee calculation
|
|
55
|
+
* - Nonce/sequence management
|
|
56
|
+
* - UTXO selection (for UTXO-based chains)
|
|
57
|
+
* - Transaction encoding
|
|
58
|
+
*
|
|
59
|
+
* @param transactionRequest - The transaction request containing parameters like recipient, amount, etc.
|
|
60
|
+
* @returns Promise resolving to the unsigned transaction and MPC payloads for signing
|
|
61
|
+
* @throws Error if transaction preparation fails
|
|
62
|
+
*/
|
|
63
|
+
abstract getMPCPayloadAndTransaction(transactionRequest: TransactionRequest): Promise<{
|
|
64
|
+
transaction: UnsignedTransaction;
|
|
65
|
+
mpcPayloads: MPCPayloads;
|
|
66
|
+
}>;
|
|
67
|
+
/**
|
|
68
|
+
* Adds MPC-generated signatures to an unsigned transaction.
|
|
69
|
+
* The signatures are applied according to the chain's specific signing scheme.
|
|
70
|
+
*
|
|
71
|
+
* @param params - Parameters for adding signatures
|
|
72
|
+
* @param params.transaction - The unsigned transaction to sign
|
|
73
|
+
* @param params.mpcSignatures - Array of RSV signatures generated through MPC
|
|
74
|
+
* @returns The serialized signed transaction ready for broadcast
|
|
75
|
+
* @throws Error if signature application fails
|
|
76
|
+
*/
|
|
77
|
+
abstract addSignature(params: {
|
|
78
|
+
transaction: UnsignedTransaction;
|
|
79
|
+
mpcSignatures: RSVSignature[];
|
|
80
|
+
}): string;
|
|
81
|
+
/**
|
|
82
|
+
* Broadcasts a signed transaction to the network.
|
|
83
|
+
*
|
|
84
|
+
* @param txSerialized - The serialized signed transaction
|
|
85
|
+
* @returns Promise resolving to the transaction hash/ID
|
|
86
|
+
* @throws Error if broadcast fails or transaction is rejected
|
|
87
|
+
*/
|
|
88
|
+
abstract broadcastTx(txSerialized: string): Promise<string>;
|
|
89
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core interface for blockchain implementations.
|
|
3
|
+
* Provides a standardized way to interact with different blockchain networks through a common set of methods.
|
|
4
|
+
*
|
|
5
|
+
* @typeParam TransactionRequest - The type of transaction request specific to the blockchain
|
|
6
|
+
* @typeParam UnsignedTransaction - The type of unsigned transaction specific to the blockchain
|
|
7
|
+
*/
|
|
8
|
+
export class Chain {
|
|
9
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type BN from 'bn.js';
|
|
2
|
+
import type { RSVSignature, UncompressedPubKeySEC1 } from '@chains';
|
|
3
|
+
/**
|
|
4
|
+
* Arguments for the sign method
|
|
5
|
+
*/
|
|
6
|
+
export interface SignArgs {
|
|
7
|
+
/** The payload to sign as an array of 32 bytes */
|
|
8
|
+
payload: number[];
|
|
9
|
+
/** The derivation path for key generation */
|
|
10
|
+
path: string;
|
|
11
|
+
/** Version of the key to use */
|
|
12
|
+
key_version: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Abstract class defining the interface for chain signature contracts.
|
|
16
|
+
* This contract handles MPC (Multi-Party Computation) operations for secure key derivation and signing.
|
|
17
|
+
* It serves as a bridge between the blockchain implementations and the MPC infrastructure.
|
|
18
|
+
*/
|
|
19
|
+
export declare abstract class ChainSignatureContract {
|
|
20
|
+
/**
|
|
21
|
+
* Gets the current signature deposit required by the contract.
|
|
22
|
+
* This deposit amount helps manage network congestion.
|
|
23
|
+
*
|
|
24
|
+
* @returns Promise resolving to the required deposit amount as a BigNumber
|
|
25
|
+
* @throws Error if the deposit check fails
|
|
26
|
+
*/
|
|
27
|
+
abstract getCurrentSignatureDeposit(): Promise<BN>;
|
|
28
|
+
/**
|
|
29
|
+
* Gets the public key associated with this contract.
|
|
30
|
+
* This is the root public key from which child keys can be derived.
|
|
31
|
+
*
|
|
32
|
+
* @returns Promise resolving to the uncompressed public key
|
|
33
|
+
* @throws Error if public key retrieval fails
|
|
34
|
+
*/
|
|
35
|
+
abstract getPublicKey(): Promise<UncompressedPubKeySEC1>;
|
|
36
|
+
/**
|
|
37
|
+
* Signs a payload using MPC.
|
|
38
|
+
* This is the core signing operation that coordinates with the MPC infrastructure.
|
|
39
|
+
*
|
|
40
|
+
* @param args - Arguments for the signing operation
|
|
41
|
+
* @param args.payload - The data to sign
|
|
42
|
+
* @param args.path - Derivation path for the signing key
|
|
43
|
+
* @param args.key_version - Version of the key to use
|
|
44
|
+
* @returns Promise resolving to the RSV signature
|
|
45
|
+
* @throws Error if signing fails or is rejected
|
|
46
|
+
*/
|
|
47
|
+
abstract sign(args: SignArgs & Record<string, unknown>): Promise<RSVSignature>;
|
|
48
|
+
/**
|
|
49
|
+
* Derives a child public key using a derivation path and predecessor.
|
|
50
|
+
* This method implements hierarchical deterministic key derivation.
|
|
51
|
+
*
|
|
52
|
+
* @param args - Arguments for key derivation
|
|
53
|
+
* @param args.path - The derivation path to use
|
|
54
|
+
* @param args.predecessor - The predecessor key (usually a NEAR account) that controls this derived key
|
|
55
|
+
* @returns Promise resolving to the derived uncompressed public key
|
|
56
|
+
* @throws Error if key derivation fails
|
|
57
|
+
*/
|
|
58
|
+
abstract getDerivedPublicKey(args: {
|
|
59
|
+
path: string;
|
|
60
|
+
predecessor: string;
|
|
61
|
+
} & Record<string, unknown>): Promise<UncompressedPubKeySEC1>;
|
|
62
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract class defining the interface for chain signature contracts.
|
|
3
|
+
* This contract handles MPC (Multi-Party Computation) operations for secure key derivation and signing.
|
|
4
|
+
* It serves as a bridge between the blockchain implementations and the MPC infrastructure.
|
|
5
|
+
*/
|
|
6
|
+
export class ChainSignatureContract {
|
|
7
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { MPCPayloads, RSVSignature, KeyDerivationPath, CosmosNetworkIds, CosmosTransactionRequest, CosmosUnsignedTransaction, ChainSignatureContract } from '@chains';
|
|
2
|
+
import { Chain } from '@chains';
|
|
3
|
+
/**
|
|
4
|
+
* Implementation of the Chain interface for Cosmos-based networks.
|
|
5
|
+
* Handles interactions with Cosmos SDK chains like Cosmos Hub, Osmosis, etc.
|
|
6
|
+
*/
|
|
7
|
+
export declare class Cosmos extends Chain<CosmosTransactionRequest, CosmosUnsignedTransaction> {
|
|
8
|
+
private readonly registry;
|
|
9
|
+
private readonly chainId;
|
|
10
|
+
private readonly contract;
|
|
11
|
+
private readonly endpoints?;
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new Cosmos chain instance
|
|
14
|
+
* @param params - Configuration parameters
|
|
15
|
+
* @param params.chainId - Chain id for the Cosmos network
|
|
16
|
+
* @param params.contract - Instance of the chain signature contract for MPC operations
|
|
17
|
+
* @param params.endpoints - Optional RPC and REST endpoints
|
|
18
|
+
* @param params.endpoints.rpcUrl - Optional RPC endpoint URL
|
|
19
|
+
* @param params.endpoints.restUrl - Optional REST endpoint URL
|
|
20
|
+
*/
|
|
21
|
+
constructor({ chainId, contract, endpoints, }: {
|
|
22
|
+
contract: ChainSignatureContract;
|
|
23
|
+
chainId: CosmosNetworkIds;
|
|
24
|
+
endpoints?: {
|
|
25
|
+
rpcUrl?: string;
|
|
26
|
+
restUrl?: string;
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
private parseRSVSignature;
|
|
30
|
+
private getChainInfo;
|
|
31
|
+
getBalance(address: string): Promise<string>;
|
|
32
|
+
deriveAddressAndPublicKey(predecessor: string, path: KeyDerivationPath): Promise<{
|
|
33
|
+
address: string;
|
|
34
|
+
publicKey: string;
|
|
35
|
+
}>;
|
|
36
|
+
setTransaction(transaction: CosmosUnsignedTransaction, storageKey: string): void;
|
|
37
|
+
getTransaction(storageKey: string, options?: {
|
|
38
|
+
remove?: boolean;
|
|
39
|
+
}): CosmosUnsignedTransaction | undefined;
|
|
40
|
+
getMPCPayloadAndTransaction(transactionRequest: CosmosTransactionRequest): Promise<{
|
|
41
|
+
transaction: CosmosUnsignedTransaction;
|
|
42
|
+
mpcPayloads: MPCPayloads;
|
|
43
|
+
}>;
|
|
44
|
+
addSignature({ transaction, mpcSignatures, }: {
|
|
45
|
+
transaction: CosmosUnsignedTransaction;
|
|
46
|
+
mpcSignatures: RSVSignature[];
|
|
47
|
+
}): string;
|
|
48
|
+
broadcastTx(txSerialized: string): Promise<string>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { encodeSecp256k1Pubkey } from '@cosmjs/amino';
|
|
2
|
+
import { ripemd160, sha256 } from '@cosmjs/crypto';
|
|
3
|
+
import { toBase64, fromBase64, fromHex } from '@cosmjs/encoding';
|
|
4
|
+
import { Registry, makeSignBytes, encodePubkey, makeAuthInfoBytes, makeSignDoc, } from '@cosmjs/proto-signing';
|
|
5
|
+
import { GasPrice, StargateClient, calculateFee } from '@cosmjs/stargate';
|
|
6
|
+
import { bech32 } from 'bech32';
|
|
7
|
+
import { SignMode } from 'cosmjs-types/cosmos/tx/signing/v1beta1/signing';
|
|
8
|
+
import { TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
|
|
9
|
+
import { Chain, utils } from '@chains';
|
|
10
|
+
import { fetchChainInfo } from '@chains/Cosmos/utils';
|
|
11
|
+
/**
|
|
12
|
+
* Implementation of the Chain interface for Cosmos-based networks.
|
|
13
|
+
* Handles interactions with Cosmos SDK chains like Cosmos Hub, Osmosis, etc.
|
|
14
|
+
*/
|
|
15
|
+
export class Cosmos extends Chain {
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new Cosmos chain instance
|
|
18
|
+
* @param params - Configuration parameters
|
|
19
|
+
* @param params.chainId - Chain id for the Cosmos network
|
|
20
|
+
* @param params.contract - Instance of the chain signature contract for MPC operations
|
|
21
|
+
* @param params.endpoints - Optional RPC and REST endpoints
|
|
22
|
+
* @param params.endpoints.rpcUrl - Optional RPC endpoint URL
|
|
23
|
+
* @param params.endpoints.restUrl - Optional REST endpoint URL
|
|
24
|
+
*/
|
|
25
|
+
constructor({ chainId, contract, endpoints, }) {
|
|
26
|
+
super();
|
|
27
|
+
this.contract = contract;
|
|
28
|
+
this.registry = new Registry();
|
|
29
|
+
this.chainId = chainId;
|
|
30
|
+
this.endpoints = endpoints;
|
|
31
|
+
}
|
|
32
|
+
parseRSVSignature(rsvSignature) {
|
|
33
|
+
return new Uint8Array([
|
|
34
|
+
...fromHex(rsvSignature.r),
|
|
35
|
+
...fromHex(rsvSignature.s),
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
38
|
+
async getChainInfo() {
|
|
39
|
+
return {
|
|
40
|
+
...(await fetchChainInfo(this.chainId)),
|
|
41
|
+
...this.endpoints,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async getBalance(address) {
|
|
45
|
+
try {
|
|
46
|
+
const { restUrl, denom, decimals } = await this.getChainInfo();
|
|
47
|
+
const response = await fetch(`${restUrl}/cosmos/bank/v1beta1/balances/${address}`);
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
50
|
+
}
|
|
51
|
+
const data = (await response.json());
|
|
52
|
+
const balance = data.balances.find((b) => b.denom === denom);
|
|
53
|
+
const amount = balance?.amount ?? '0';
|
|
54
|
+
const formattedBalance = (parseInt(amount) / Math.pow(10, decimals)).toString();
|
|
55
|
+
return formattedBalance;
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error('Failed to fetch Cosmos balance:', error);
|
|
59
|
+
throw new Error('Failed to fetch Cosmos balance');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async deriveAddressAndPublicKey(predecessor, path) {
|
|
63
|
+
const { prefix } = await this.getChainInfo();
|
|
64
|
+
const uncompressedPubKey = await this.contract.getDerivedPublicKey({
|
|
65
|
+
path,
|
|
66
|
+
predecessor,
|
|
67
|
+
});
|
|
68
|
+
if (!uncompressedPubKey) {
|
|
69
|
+
throw new Error('Failed to get derived public key');
|
|
70
|
+
}
|
|
71
|
+
const derivedKey = utils.compressPubKey(uncompressedPubKey);
|
|
72
|
+
const pubKeySha256 = sha256(fromHex(derivedKey));
|
|
73
|
+
const ripemd160Hash = ripemd160(pubKeySha256);
|
|
74
|
+
const address = bech32.encode(prefix, bech32.toWords(ripemd160Hash));
|
|
75
|
+
return { address, publicKey: derivedKey };
|
|
76
|
+
}
|
|
77
|
+
setTransaction(transaction, storageKey) {
|
|
78
|
+
const serialized = TxRaw.encode(transaction).finish();
|
|
79
|
+
window.localStorage.setItem(storageKey, toBase64(serialized));
|
|
80
|
+
}
|
|
81
|
+
getTransaction(storageKey, options) {
|
|
82
|
+
const serialized = window.localStorage.getItem(storageKey);
|
|
83
|
+
if (!serialized)
|
|
84
|
+
return undefined;
|
|
85
|
+
if (options?.remove) {
|
|
86
|
+
window.localStorage.removeItem(storageKey);
|
|
87
|
+
}
|
|
88
|
+
return TxRaw.decode(fromBase64(serialized));
|
|
89
|
+
}
|
|
90
|
+
async getMPCPayloadAndTransaction(transactionRequest) {
|
|
91
|
+
const { denom, rpcUrl, gasPrice } = await this.getChainInfo();
|
|
92
|
+
const publicKeyBytes = fromHex(transactionRequest.publicKey);
|
|
93
|
+
const gasLimit = transactionRequest.gas || 200000;
|
|
94
|
+
const fee = calculateFee(gasLimit, GasPrice.fromString(`${gasPrice}${denom}`));
|
|
95
|
+
const client = await StargateClient.connect(rpcUrl);
|
|
96
|
+
const accountOnChain = await client.getAccount(transactionRequest.address);
|
|
97
|
+
if (!accountOnChain) {
|
|
98
|
+
throw new Error(`Account ${transactionRequest.address} does not exist on chain`);
|
|
99
|
+
}
|
|
100
|
+
const { accountNumber, sequence } = accountOnChain;
|
|
101
|
+
const txBodyEncodeObject = {
|
|
102
|
+
typeUrl: '/cosmos.tx.v1beta1.TxBody',
|
|
103
|
+
value: {
|
|
104
|
+
messages: transactionRequest.messages,
|
|
105
|
+
memo: transactionRequest.memo || '',
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
const txBodyBytes = this.registry.encode(txBodyEncodeObject);
|
|
109
|
+
const pubkey = encodePubkey(encodeSecp256k1Pubkey(publicKeyBytes));
|
|
110
|
+
// TODO: Allow caller to provide: multiple signers, fee payer, fee granter
|
|
111
|
+
const authInfoBytes = makeAuthInfoBytes([
|
|
112
|
+
{
|
|
113
|
+
pubkey,
|
|
114
|
+
sequence,
|
|
115
|
+
},
|
|
116
|
+
], fee.amount, Number(fee.gas), undefined, undefined, SignMode.SIGN_MODE_DIRECT);
|
|
117
|
+
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, this.chainId, accountNumber);
|
|
118
|
+
const signBytes = makeSignBytes(signDoc);
|
|
119
|
+
const payload = Array.from(sha256(signBytes));
|
|
120
|
+
return {
|
|
121
|
+
transaction: TxRaw.fromPartial({
|
|
122
|
+
bodyBytes: txBodyBytes,
|
|
123
|
+
authInfoBytes,
|
|
124
|
+
signatures: [],
|
|
125
|
+
}),
|
|
126
|
+
mpcPayloads: [
|
|
127
|
+
{
|
|
128
|
+
index: 0,
|
|
129
|
+
payload,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
addSignature({ transaction, mpcSignatures, }) {
|
|
135
|
+
// Allow support for multi-sig but the package only supports single-sig
|
|
136
|
+
transaction.signatures = mpcSignatures.map((sig) => this.parseRSVSignature(sig));
|
|
137
|
+
const txBytes = TxRaw.encode(transaction).finish();
|
|
138
|
+
return Buffer.from(txBytes).toString('hex');
|
|
139
|
+
}
|
|
140
|
+
async broadcastTx(txSerialized) {
|
|
141
|
+
try {
|
|
142
|
+
const { rpcUrl } = await this.getChainInfo();
|
|
143
|
+
const client = await StargateClient.connect(rpcUrl);
|
|
144
|
+
const txBytes = fromHex(txSerialized);
|
|
145
|
+
const broadcastResponse = await client.broadcastTx(txBytes);
|
|
146
|
+
if (broadcastResponse.code !== 0) {
|
|
147
|
+
throw new Error(`Broadcast error: ${broadcastResponse.rawLog}`);
|
|
148
|
+
}
|
|
149
|
+
return broadcastResponse.transactionHash;
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
console.error('Transaction broadcast failed:', error);
|
|
153
|
+
throw new Error('Failed to broadcast transaction.');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type EncodeObject } from '@cosmjs/proto-signing';
|
|
2
|
+
import { type TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
|
|
3
|
+
export type CosmosNetworkIds = string;
|
|
4
|
+
export type CosmosUnsignedTransaction = TxRaw;
|
|
5
|
+
export interface CosmosTransactionRequest {
|
|
6
|
+
address: string;
|
|
7
|
+
publicKey: string;
|
|
8
|
+
messages: EncodeObject[];
|
|
9
|
+
memo?: string;
|
|
10
|
+
gas?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface BalanceResponse {
|
|
13
|
+
balances: Array<{
|
|
14
|
+
denom: string;
|
|
15
|
+
amount: string;
|
|
16
|
+
}>;
|
|
17
|
+
pagination: {
|
|
18
|
+
next_key: string | null;
|
|
19
|
+
total: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface ChainInfo {
|
|
23
|
+
prefix: string;
|
|
24
|
+
denom: string;
|
|
25
|
+
rpcUrl: string;
|
|
26
|
+
restUrl: string;
|
|
27
|
+
expectedChainId: string;
|
|
28
|
+
gasPrice: number;
|
|
29
|
+
decimals: number;
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { chains, assets } from 'chain-registry';
|
|
2
|
+
export const fetchChainInfo = async (chainId) => {
|
|
3
|
+
const chainInfo = chains.find((chain) => chain.chain_id === chainId);
|
|
4
|
+
if (!chainInfo) {
|
|
5
|
+
throw new Error(`Chain info not found for chainId: ${chainId}`);
|
|
6
|
+
}
|
|
7
|
+
const { bech32_prefix: prefix, chain_id: expectedChainId } = chainInfo;
|
|
8
|
+
const denom = chainInfo.staking?.staking_tokens?.[0]?.denom;
|
|
9
|
+
const rpcUrl = chainInfo.apis?.rpc?.[0]?.address;
|
|
10
|
+
const restUrl = chainInfo.apis?.rest?.[0]?.address;
|
|
11
|
+
const gasPrice = chainInfo.fees?.fee_tokens?.[0]?.average_gas_price;
|
|
12
|
+
if (!prefix ||
|
|
13
|
+
!denom ||
|
|
14
|
+
!rpcUrl ||
|
|
15
|
+
!restUrl ||
|
|
16
|
+
!expectedChainId ||
|
|
17
|
+
gasPrice === undefined) {
|
|
18
|
+
throw new Error(`Missing required chain information for ${chainInfo.chain_name}`);
|
|
19
|
+
}
|
|
20
|
+
const assetList = assets.find((asset) => asset.chain_name === chainInfo.chain_name);
|
|
21
|
+
const asset = assetList?.assets.find((asset) => asset.base === denom);
|
|
22
|
+
const decimals = asset?.denom_units.find((unit) => unit.denom === asset.display)?.exponent;
|
|
23
|
+
if (decimals === undefined) {
|
|
24
|
+
throw new Error(`Could not find decimals for ${denom} on chain ${chainInfo.chain_name}`);
|
|
25
|
+
}
|
|
26
|
+
return { prefix, denom, rpcUrl, restUrl, expectedChainId, gasPrice, decimals };
|
|
27
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type MPCPayloads, type RSVSignature, type KeyDerivationPath, type EVMTransactionRequest, type EVMUnsignedTransaction, type ChainSignatureContract } from '@chains';
|
|
2
|
+
import { Chain } from '@chains';
|
|
3
|
+
/**
|
|
4
|
+
* Implementation of the Chain interface for EVM-compatible networks.
|
|
5
|
+
* Handles interactions with Ethereum Virtual Machine based blockchains like Ethereum, BSC, Polygon, etc.
|
|
6
|
+
*/
|
|
7
|
+
export declare class EVM extends Chain<EVMTransactionRequest, EVMUnsignedTransaction> {
|
|
8
|
+
private readonly provider;
|
|
9
|
+
private readonly contract;
|
|
10
|
+
/**
|
|
11
|
+
* Creates a new EVM chain instance
|
|
12
|
+
* @param params - Configuration parameters
|
|
13
|
+
* @param params.rpcUrl - URL of the EVM JSON-RPC provider (e.g., Infura endpoint)
|
|
14
|
+
* @param params.contract - Instance of the chain signature contract for MPC operations
|
|
15
|
+
*/
|
|
16
|
+
constructor({ rpcUrl, contract, }: {
|
|
17
|
+
rpcUrl: string;
|
|
18
|
+
contract: ChainSignatureContract;
|
|
19
|
+
});
|
|
20
|
+
private attachGasAndNonce;
|
|
21
|
+
private parseSignature;
|
|
22
|
+
deriveAddressAndPublicKey(predecessor: string, path: KeyDerivationPath): Promise<{
|
|
23
|
+
address: string;
|
|
24
|
+
publicKey: string;
|
|
25
|
+
}>;
|
|
26
|
+
getBalance(address: string): Promise<string>;
|
|
27
|
+
setTransaction(transaction: EVMUnsignedTransaction, storageKey: string): void;
|
|
28
|
+
getTransaction(storageKey: string, options?: {
|
|
29
|
+
remove?: boolean;
|
|
30
|
+
}): EVMUnsignedTransaction | undefined;
|
|
31
|
+
getMPCPayloadAndTransaction(transactionRequest: EVMTransactionRequest): Promise<{
|
|
32
|
+
transaction: EVMUnsignedTransaction;
|
|
33
|
+
mpcPayloads: MPCPayloads;
|
|
34
|
+
}>;
|
|
35
|
+
addSignature({ transaction, mpcSignatures, }: {
|
|
36
|
+
transaction: EVMUnsignedTransaction;
|
|
37
|
+
mpcSignatures: RSVSignature[];
|
|
38
|
+
}): string;
|
|
39
|
+
broadcastTx(txSerialized: string): Promise<string>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { fromHex } from '@cosmjs/encoding';
|
|
2
|
+
import { ethers, keccak256 } from 'ethers';
|
|
3
|
+
import { Chain, fetchEVMFeeProperties } from '@chains';
|
|
4
|
+
/**
|
|
5
|
+
* Implementation of the Chain interface for EVM-compatible networks.
|
|
6
|
+
* Handles interactions with Ethereum Virtual Machine based blockchains like Ethereum, BSC, Polygon, etc.
|
|
7
|
+
*/
|
|
8
|
+
export class EVM extends Chain {
|
|
9
|
+
/**
|
|
10
|
+
* Creates a new EVM chain instance
|
|
11
|
+
* @param params - Configuration parameters
|
|
12
|
+
* @param params.rpcUrl - URL of the EVM JSON-RPC provider (e.g., Infura endpoint)
|
|
13
|
+
* @param params.contract - Instance of the chain signature contract for MPC operations
|
|
14
|
+
*/
|
|
15
|
+
constructor({ rpcUrl, contract, }) {
|
|
16
|
+
super();
|
|
17
|
+
this.contract = contract;
|
|
18
|
+
this.provider = new ethers.JsonRpcProvider(rpcUrl);
|
|
19
|
+
}
|
|
20
|
+
async attachGasAndNonce(transaction) {
|
|
21
|
+
const fees = await fetchEVMFeeProperties(this.provider._getConnection().url, transaction);
|
|
22
|
+
const nonce = await this.provider.getTransactionCount(transaction.from, 'latest');
|
|
23
|
+
const { from, ...rest } = transaction;
|
|
24
|
+
return {
|
|
25
|
+
...fees,
|
|
26
|
+
chainId: this.provider._network.chainId,
|
|
27
|
+
nonce,
|
|
28
|
+
type: 2,
|
|
29
|
+
...rest,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
parseSignature(signature) {
|
|
33
|
+
return ethers.Signature.from({
|
|
34
|
+
r: `0x${signature.r}`,
|
|
35
|
+
s: `0x${signature.s}`,
|
|
36
|
+
v: signature.v,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async deriveAddressAndPublicKey(predecessor, path) {
|
|
40
|
+
const uncompressedPubKey = await this.contract.getDerivedPublicKey({
|
|
41
|
+
path,
|
|
42
|
+
predecessor,
|
|
43
|
+
});
|
|
44
|
+
if (!uncompressedPubKey) {
|
|
45
|
+
throw new Error('Failed to get derived public key');
|
|
46
|
+
}
|
|
47
|
+
const publicKeyNoPrefix = uncompressedPubKey.startsWith('04')
|
|
48
|
+
? uncompressedPubKey.substring(2)
|
|
49
|
+
: uncompressedPubKey;
|
|
50
|
+
const hash = ethers.keccak256(fromHex(publicKeyNoPrefix));
|
|
51
|
+
return {
|
|
52
|
+
address: `0x${hash.substring(hash.length - 40)}`,
|
|
53
|
+
publicKey: uncompressedPubKey,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async getBalance(address) {
|
|
57
|
+
try {
|
|
58
|
+
const balance = await this.provider.getBalance(address);
|
|
59
|
+
return ethers.formatEther(balance);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error(`Failed to fetch balance for address ${address}:`, error);
|
|
63
|
+
throw new Error('Failed to fetch balance.');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
setTransaction(transaction, storageKey) {
|
|
67
|
+
const serializedTransaction = JSON.stringify(transaction, (_, value) => typeof value === 'bigint' ? value.toString() : value);
|
|
68
|
+
window.localStorage.setItem(storageKey, serializedTransaction);
|
|
69
|
+
}
|
|
70
|
+
getTransaction(storageKey, options) {
|
|
71
|
+
const txSerialized = window.localStorage.getItem(storageKey);
|
|
72
|
+
if (options?.remove) {
|
|
73
|
+
window.localStorage.removeItem(storageKey);
|
|
74
|
+
}
|
|
75
|
+
return txSerialized ? JSON.parse(txSerialized) : undefined;
|
|
76
|
+
}
|
|
77
|
+
async getMPCPayloadAndTransaction(transactionRequest) {
|
|
78
|
+
const transaction = await this.attachGasAndNonce(transactionRequest);
|
|
79
|
+
const txSerialized = ethers.Transaction.from(transaction).unsignedSerialized;
|
|
80
|
+
const transactionHash = keccak256(txSerialized);
|
|
81
|
+
const txHash = Array.from(ethers.getBytes(transactionHash));
|
|
82
|
+
return {
|
|
83
|
+
transaction,
|
|
84
|
+
mpcPayloads: [
|
|
85
|
+
{
|
|
86
|
+
index: 0,
|
|
87
|
+
payload: txHash,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
addSignature({ transaction, mpcSignatures, }) {
|
|
93
|
+
return ethers.Transaction.from({
|
|
94
|
+
...transaction,
|
|
95
|
+
signature: this.parseSignature(mpcSignatures[0]),
|
|
96
|
+
}).serialized;
|
|
97
|
+
}
|
|
98
|
+
async broadcastTx(txSerialized) {
|
|
99
|
+
try {
|
|
100
|
+
const txResponse = await this.provider.broadcastTransaction(txSerialized);
|
|
101
|
+
return txResponse.hash;
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.error('Transaction broadcast failed:', error);
|
|
105
|
+
throw new Error('Failed to broadcast transaction.');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
|
+
export async function fetchEVMFeeProperties(providerUrl, transaction) {
|
|
3
|
+
const provider = new ethers.JsonRpcProvider(providerUrl);
|
|
4
|
+
const gasLimit = await provider.estimateGas(transaction);
|
|
5
|
+
const feeData = await provider.getFeeData();
|
|
6
|
+
const maxFeePerGas = feeData.maxFeePerGas ?? ethers.parseUnits('10', 'gwei');
|
|
7
|
+
const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? ethers.parseUnits('10', 'gwei');
|
|
8
|
+
return {
|
|
9
|
+
gasLimit,
|
|
10
|
+
maxFeePerGas,
|
|
11
|
+
maxPriorityFeePerGas,
|
|
12
|
+
maxFee: maxFeePerGas * gasLimit,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { Chain } from './Chain';
|
|
2
|
+
export { ChainSignatureContract, type SignArgs } from './ChainSignatureContract';
|
|
3
|
+
export * from './types';
|
|
4
|
+
export * as utils from './utils';
|
|
5
|
+
export { EVM } from './EVM/EVM';
|
|
6
|
+
export { fetchEVMFeeProperties } from './EVM/utils';
|
|
7
|
+
export type { EVMTransactionRequest, EVMUnsignedTransaction } from './EVM/types';
|
|
8
|
+
export { Bitcoin } from './Bitcoin/Bitcoin';
|
|
9
|
+
export { BTCRpcAdapters, BTCRpcAdapter } from './Bitcoin/BTCRpcAdapter';
|
|
10
|
+
export type { BTCTransactionRequest, BTCUnsignedTransaction, BTCTransaction, BTCOutput, BTCInput, BTCNetworkIds, } from './Bitcoin/types';
|
|
11
|
+
export { Cosmos } from './Cosmos/Cosmos';
|
|
12
|
+
export type { CosmosNetworkIds, CosmosTransactionRequest, CosmosUnsignedTransaction, } from './Cosmos/types';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { Chain } from './Chain';
|
|
2
|
+
export { ChainSignatureContract } from './ChainSignatureContract';
|
|
3
|
+
export * from './types';
|
|
4
|
+
export * as utils from './utils';
|
|
5
|
+
// EVM
|
|
6
|
+
export { EVM } from './EVM/EVM';
|
|
7
|
+
export { fetchEVMFeeProperties } from './EVM/utils';
|
|
8
|
+
// Bitcoin
|
|
9
|
+
export { Bitcoin } from './Bitcoin/Bitcoin';
|
|
10
|
+
export { BTCRpcAdapters, BTCRpcAdapter } from './Bitcoin/BTCRpcAdapter';
|
|
11
|
+
// Cosmos
|
|
12
|
+
export { Cosmos } from './Cosmos/Cosmos';
|