diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..d41759e
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,16 @@
+{
+ "image": "mcr.microsoft.com/devcontainers/universal:2.11.2",
+ //"forwardPorts": [3000],
+ "customizations": {
+ // Configure properties specific to VS Code.
+ "vscode": {
+ // Add the IDs of extensions you want installed when the container is created.
+ "extensions": [
+ "markdown.showPreview",
+ "bierner.markdown-mermaid",
+ "aaron-bond.better-comments",
+ "ms-vscode.go"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 399baad..e9a0d59 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,7 @@
*.crt
*.key
.vscode
-init
\ No newline at end of file
+init
+**/key.txt
+**/.env
+**/node_modules
\ No newline at end of file
diff --git a/README.md b/README.md
index ab30d41..5bb7dd3 100644
--- a/README.md
+++ b/README.md
@@ -1,58 +1,12 @@
+# Blockdaemon Staking API demos using different wallets and SDKs
-## Sample go staking app demo
+This repository contains multiple applications. Below are the links to the individual application folders and their README files.
+To get started, click on the links for installation and usage instructions.
+## Solana Staking
+- [Stake deposit from Builder Vault wallet with TypeScript SDK](./solana-staking/buildervault-wallet/nodejs/README.md)
-```mermaid
-sequenceDiagram
- autonumber
- participant StakeClient as Sample stake
client application
- participant StakeAPI as Stake Intent API
- participant Blockchain as Blockchain RPC API
- box Builder Vault
- participant TSM1 as MPC Wallet
(private key share 1)
- participant TSM2 as MPC Wallet
(private key share 2)
- end
+## Ethereum Staking
+- [Stake deposit from Builder Vault wallet with Golang SDK](./ethereum-staking/buildervault-wallet/golang/README.md)
+- [Stake deposit from Builder Vault wallet with TypeScript SDK](./ethereum-staking/buildervault-wallet/nodejs/README.md)
- StakeClient ->> StakeAPI: get StakeIntent unsigned tx data
(amount, withdrawal & recipient address)
- StakeClient ->> Blockchain: get blockchain inputs (gas, nonce) for new tx
(sender wallet)
- StakeClient ->> StakeClient: construct unsigned tx
- StakeClient ->> TSM1: request signature (unsigned tx)
- TSM1 -->> StakeClient: return partial signature
- StakeClient ->> TSM2: request signature (unsigned tx)
- TSM2 -->> StakeClient: return partial signature
- StakeClient ->> StakeClient: combine partial signatures
- StakeClient ->> Blockchain: broadcast signed tx
(signed tx, deposit contract)
-```
-
-
\ No newline at end of file
diff --git a/ethereum-staking/buildervault-wallet/golang/README.md b/ethereum-staking/buildervault-wallet/golang/README.md
new file mode 100644
index 0000000..ab30d41
--- /dev/null
+++ b/ethereum-staking/buildervault-wallet/golang/README.md
@@ -0,0 +1,58 @@
+
+## Sample go staking app demo
+
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant StakeClient as Sample stake
client application
+ participant StakeAPI as Stake Intent API
+ participant Blockchain as Blockchain RPC API
+ box Builder Vault
+ participant TSM1 as MPC Wallet
(private key share 1)
+ participant TSM2 as MPC Wallet
(private key share 2)
+ end
+
+ StakeClient ->> StakeAPI: get StakeIntent unsigned tx data
(amount, withdrawal & recipient address)
+ StakeClient ->> Blockchain: get blockchain inputs (gas, nonce) for new tx
(sender wallet)
+ StakeClient ->> StakeClient: construct unsigned tx
+ StakeClient ->> TSM1: request signature (unsigned tx)
+ TSM1 -->> StakeClient: return partial signature
+ StakeClient ->> TSM2: request signature (unsigned tx)
+ TSM2 -->> StakeClient: return partial signature
+ StakeClient ->> StakeClient: combine partial signatures
+ StakeClient ->> Blockchain: broadcast signed tx
(signed tx, deposit contract)
+```
+
+
\ No newline at end of file
diff --git a/go.mod b/ethereum-staking/buildervault-wallet/golang/go.mod
similarity index 100%
rename from go.mod
rename to ethereum-staking/buildervault-wallet/golang/go.mod
diff --git a/go.sum b/ethereum-staking/buildervault-wallet/golang/go.sum
similarity index 100%
rename from go.sum
rename to ethereum-staking/buildervault-wallet/golang/go.sum
diff --git a/main.go b/ethereum-staking/buildervault-wallet/golang/main.go
similarity index 93%
rename from main.go
rename to ethereum-staking/buildervault-wallet/golang/main.go
index 989da03..0730323 100644
--- a/main.go
+++ b/ethereum-staking/buildervault-wallet/golang/main.go
@@ -16,7 +16,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
- "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"gitlab.com/Blockdaemon/go-tsm-sdkv2/tsm" // Builder Vault MPC SDK for wallet management
"golang.org/x/sync/errgroup"
@@ -129,7 +128,13 @@ func craftTx(client *ethclient.Client, ethereumSenderAddress string, contractAdd
Data: common.FromHex(txData),
})
- fmt.Println("\nCrafted unsigned transaction:\n", "Nonce:", unsignedTx.Nonce(), "\n GasFeeCap:", unsignedTx.GasFeeCap(), "\n Gas:", unsignedTx.Gas(), "\n To:", unsignedTx.To().String(), "\n Value:", unsignedTx.Value())
+ fmt.Println("\nCrafted unsigned transaction values:\n", "Nonce:", unsignedTx.Nonce(), "\n GasFeeCap:", unsignedTx.GasFeeCap(), "\n Gas:", unsignedTx.Gas(), "\n To:", unsignedTx.To().String(), "\n Value amount:", unsignedTx.Value(), "\n Hash:", unsignedTx.Hash())
+
+ raw, err := unsignedTx.MarshalBinary()
+ if err != nil {
+ panic(err)
+ }
+ fmt.Printf("\nCrafted unsigned transaction (hex encoded): 0x%x", raw)
// create a NewLondonSigner for EIP 1559 transactions
signer := types.NewLondonSigner(chainID)
@@ -193,7 +198,7 @@ func signTx(unsignedTxHash []byte) []byte {
copy(sigBytes[0:32], signature.R())
copy(sigBytes[32:64], signature.S())
sigBytes[64] = byte(signature.RecoveryID())
- fmt.Println("\nTransaction signature:\n", hex.EncodeToString(sigBytes))
+ fmt.Println("\n\nTransaction signature (hex encoded):\n", hex.EncodeToString(sigBytes))
return sigBytes
}
@@ -206,11 +211,11 @@ func sendTx(client *ethclient.Client, chainID *big.Int, unsignedTx *types.Transa
panic(err)
}
- raw, err := rlp.EncodeToBytes(signedTx)
+ raw, err := signedTx.MarshalBinary()
if err != nil {
panic(err)
}
- fmt.Printf("\nSigned raw transaction: 0x%x", raw)
+ fmt.Printf("\nSigned raw transaction (RLP encoded): 0x%x", raw)
err = client.SendTransaction(context.Background(), signedTx)
if err != nil {
diff --git a/ethereum-staking/buildervault-wallet/nodejs/.env.example b/ethereum-staking/buildervault-wallet/nodejs/.env.example
new file mode 100644
index 0000000..fba212a
--- /dev/null
+++ b/ethereum-staking/buildervault-wallet/nodejs/.env.example
@@ -0,0 +1,6 @@
+# Blockdaemon Stake
+BLOCKDAEMON_API_KEY=
+BLOCKDAEMON_STAKE_API_KEY=
+ETHEREUM_NETWORK=holesky # mainnet | holesky
+ETHEREUM_WITHDRAWAL_ADDRESS=
+PLAN_ID= # Optional. If provided, will use a specific validator plan.
diff --git a/ethereum-staking/buildervault-wallet/nodejs/README.md b/ethereum-staking/buildervault-wallet/nodejs/README.md
new file mode 100644
index 0000000..91eaa9c
--- /dev/null
+++ b/ethereum-staking/buildervault-wallet/nodejs/README.md
@@ -0,0 +1,66 @@
+
+# Ethereum staking on Testnet with Builder Vault wallet
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant StakeClient as Sample stake
client application
+ participant StakeAPI as Stake Intent API
+ participant Blockchain as Blockchain RPC API
+ box Builder Vault
+ participant TSM1 as MPC Wallet
(private key share 1)
+ participant TSM2 as MPC Wallet
(private key share 2)
+ end
+
+ StakeClient ->> StakeAPI: get StakeIntent unsigned tx data
(amount, withdrawal & recipient address)
+ StakeClient ->> Blockchain: get blockchain inputs (gas, nonce) for new tx
(sender wallet)
+ StakeClient ->> StakeClient: construct unsigned tx
+ StakeClient ->> TSM1: request signature (unsigned tx)
+ TSM1 -->> StakeClient: return partial signature
+ StakeClient ->> TSM2: request signature (unsigned tx)
+ TSM2 -->> StakeClient: return partial signature
+ StakeClient ->> StakeClient: combine partial signatures
+ StakeClient ->> Blockchain: broadcast signed tx
(signed tx, deposit contract)
+```
+
+### Prerequisites
+ - [Node.js](https://nodejs.org/en/download/package-manager) or use [code-spaces](https://codespaces.new/Blockdaemon/demo-buildervault-stakingAPI?quickstart=1)
+ - Register for a demo Builder Vault tenant: https://www.blockdaemon.com/get-started/builder-vault-sandbox-registration
+ - Download SDK bundle provided in registration email (extract authentication certificates)
+ - Place Builder Vault authentication certificate key-pair `client.crt` & `client.key` in this nodejs folder
+ - Register for free Blockdaemon [RPC API key](https://docs.blockdaemon.com/reference/get-started-rpc#step-1-sign-up-for-an-api-key) and set in .env as BLOCKDAEMON_API_KEY
+ - Register for free Blockdaemon [Staking API key](https://docs.blockdaemon.com/reference/get-started-staking-api#step-1-sign-up-for-an-api-key) and set in .env as BLOCKDAEMON_STAKE_API_KEY
+ - Speak to your CSM about getting credentials to the Blockdaemon nexus.sepior.net repo for the nodejs SDK.
+
+### Step 1. Set environment variables in .env
+```shell
+cd ethereum-staking/buildervault-wallet/nodejs/
+cp .env.example .env
+```
+- update .env with API keys
+
+### Step 2. Install package dependancies
+- replace NEXUS_USERNAME & NEXUS_PASSWORD with credential provided by your CSM
+```shell
+npm config set @sepior:registry=https://nexus.sepior.net/repository/sepior-nodejs-tsm-sdk-group/
+npm config set //nexus.sepior.net/repository/sepior-nodejs-tsm-sdk-group/:username=NEXUS_USERNAME
+npm config set //nexus.sepior.net/repository/sepior-nodejs-tsm-sdk-group/:\_password=`echo -n 'NEXUS_PASSWORD' | base64`
+npm install
+```
+
+### Step 3. Launch ethereum-stake-bv.ts to auto-create the Builder Vault wallet address on first run
+```shell
+npm run start ethereum-stake-bv.ts
+```
+- note, on first run this step will fail as the wallet address has no funds
+- copy the new Ethereum wallet address and fund the account
+
+### Step 4. Fund the new Ethereum wallet address with 33 ETH using faucets below
+ - https://holesky-faucet.pk910.de/#/
+
+### Step 5. Launch ethereum-stake-bv.ts to generate the Stake Intent request, sign the request with BuilderVault and broadcast the transaction
+```shell
+npm run start ethereum-stake-bv.ts
+```
+- (optional) decode the raw unsigned transaction to inspect the Blockdaemon provided attributes (https://rawtxdecode.in)
+- observe the confirmed transaction through the generated blockexplorer link
diff --git a/ethereum-staking/buildervault-wallet/nodejs/ethereum-stake-bv.ts b/ethereum-staking/buildervault-wallet/nodejs/ethereum-stake-bv.ts
new file mode 100644
index 0000000..656a468
--- /dev/null
+++ b/ethereum-staking/buildervault-wallet/nodejs/ethereum-stake-bv.ts
@@ -0,0 +1,359 @@
+// @ts-ignore
+import { TSMClient, Configuration, SessionConfig, curves } from "@sepior/tsmsdkv2";
+import fs from "fs";
+import { Web3 } from 'web3';
+import { keccak256, toHex, toChecksumAddress } from 'web3-utils';
+import { TransactionFactory, FeeMarketEIP1559Transaction } from 'web3-eth-accounts';
+import crypto from "crypto";
+// @ts-ignore
+import asn1 from "asn1.js";
+import 'dotenv/config'
+
+
+type CreateStakeIntentRequest = {
+ stakes: {
+ fee_recipient: string;
+ withdrawal_address: string;
+ amount: string;
+ }[];
+};
+
+type CreateStakeIntentResponse = {
+ stake_intent_id: string;
+ ethereum: {
+ stakes: {
+ stake_id: string;
+ amount: string;
+ validator_public_key: string;
+ withdrawal_credentials: string;
+ }[];
+ contract_address: string;
+ unsigned_transaction: string;
+ };
+};
+
+
+function createStakeIntent(
+ bossApiKey: string,
+ request: CreateStakeIntentRequest,
+): Promise {
+ const requestOptions = {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ 'X-API-Key': bossApiKey,
+ //'Idempotency-Key': 'C5410A37-82CD-4468-AD29-EE6BE324FF07',
+ },
+ body: JSON.stringify(request),
+ };
+
+ return fetch(
+ 'https://svc.blockdaemon.com/boss/v1/ethereum/holesky/stake-intents',
+ requestOptions,
+ ).then(response => response.json() as Promise);
+}
+
+export type EthereumSignature = {
+ r: string,
+ s: string,
+ v: BigInt,
+};
+
+
+async function main() {
+
+ const gwei = 10n ** 9n;
+
+ // Check for the required environment variables
+ if (!process.env.BLOCKDAEMON_API_KEY) {
+ throw new Error('BLOCKDAEMON_API_KEY environment variable not set');
+ }
+
+ if (!process.env.BLOCKDAEMON_STAKE_API_KEY) {
+ throw new Error('BLOCKDAEMON_STAKE_API_KEY environment variable not set');
+ }
+
+ if (!process.env.ETHEREUM_NETWORK) {
+ throw new Error('ETHEREUM_NETWORK environment variable not set.');
+ }
+
+ if (!process.env.ETHEREUM_WITHDRAWAL_ADDRESS) {
+ throw new Error('ETHEREUM_WITHDRAWAL_ADDRESS environment variable not set');
+ }
+
+ // Set buildervault endpoints
+
+ const serverMtlsPublicKeys = {
+ 0: `-----BEGIN CERTIFICATE-----\nMIICMTCCAdegAwIBAgICB+MwCgYIKoZIzj0EAwIwgaAxCzAJBgNVBAYTAlVTMRMw\nEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYDVQQHDAtMb3MgQW5nZWxlczEUMBIGA1UE\nCgwLQmxvY2tkYWVtb24xFDASBgNVBAsMC0Jsb2NrZGFlbW9uMRQwEgYDVQQDDAtC\nbG9ja2RhZW1vbjEkMCIGCSqGSIb3DQEJARYVYWRtaW5AYmxvY2tkYWVtb24uY29t\nMB4XDTI0MDIxMzE3MjE0OFoXDTI5MDIxMzE3MjE0OFowTjELMAkGA1UEBhMCVVMx\nEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC0xvcyBBbmdlbGVzMRQwEgYD\nVQQKEwtCbG9ja2RhZW1vbjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGlixcUc\nYC0ByeutoHHdi3zxWCg5iPAJcxVLvzBUdD2+XdCWEgS/xwFEef9Tl3xFdfK4iWSQ\nnjmtYMTaHMM6mfWjUjBQMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggrBgEF\nBQcDAgYIKwYBBQUHAwEwHwYDVR0jBBgwFoAUW6ouasv5oWo7MZ4ZzlE/mpbDrIMw\nCgYIKoZIzj0EAwIDSAAwRQIgSDKHZmsnylzL8kopFSeo8L6LQGxyd/NsBRb+8STI\n1cECIQChi4cl5nJgTXCBzJEHicnRk/0vl+9zq6iABMV+KTXJxA==\n-----END CERTIFICATE-----`,
+ 1: `-----BEGIN CERTIFICATE-----\nMIICMjCCAdegAwIBAgICB+MwCgYIKoZIzj0EAwIwgaAxCzAJBgNVBAYTAlVTMRMw\nEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYDVQQHDAtMb3MgQW5nZWxlczEUMBIGA1UE\nCgwLQmxvY2tkYWVtb24xFDASBgNVBAsMC0Jsb2NrZGFlbW9uMRQwEgYDVQQDDAtC\nbG9ja2RhZW1vbjEkMCIGCSqGSIb3DQEJARYVYWRtaW5AYmxvY2tkYWVtb24uY29t\nMB4XDTI0MDIxMzE3MjEzMloXDTI5MDIxMzE3MjEzMlowTjELMAkGA1UEBhMCVVMx\nEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC0xvcyBBbmdlbGVzMRQwEgYD\nVQQKEwtCbG9ja2RhZW1vbjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKz8yGcE\nYIhaQYCA2As30cRIL2rLrB2uKpcFpydE55RoI3Hw+QaeNCfR5znZQZM4bVVquT4i\nxDGhVnQKU5EQU/WjUjBQMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggrBgEF\nBQcDAgYIKwYBBQUHAwEwHwYDVR0jBBgwFoAUW6ouasv5oWo7MZ4ZzlE/mpbDrIMw\nCgYIKoZIzj0EAwIDSQAwRgIhAO9yXpssqar6IdgmEOIfAsha0ZIWG56nwE8/GbyN\nBiTaAiEAhhEClrSm/TzmWxODXamBz0pmQ9qNFsrtbGsDhLOe8O8=\n-----END CERTIFICATE-----`,
+ };
+
+ const cert0 = new crypto.X509Certificate(serverMtlsPublicKeys[0]);
+ const cert1 = new crypto.X509Certificate(serverMtlsPublicKeys[1]);
+
+ const config0 = await new Configuration("https://tsm-sandbox.prd.wallet.blockdaemon.app:8080");
+ await config0.withMTLSAuthentication("./client.key", "./client.crt", cert0.publicKey.export({type: "spki",format: "der"}));
+
+ const config1 = await new Configuration("https://tsm-sandbox.prd.wallet.blockdaemon.app:8081")
+ await config1.withMTLSAuthentication("./client.key", "./client.crt", cert1.publicKey.export({type: "spki",format: "der"}));
+
+ // Create clients for two MPC nodes
+ const clients: TSMClient[] = [
+ await TSMClient.withConfiguration(config0),
+ await TSMClient.withConfiguration(config1),
+ ];
+
+ const threshold = 1; // The security threshold for this key
+
+ const masterKeyId = await getKeyId(clients, threshold, "key.txt");
+
+ const chainPath = new Uint32Array([44, 60]);
+
+ const pkixPublicKeys: Uint8Array[] = [];
+
+ for (const [_, client] of clients.entries()) {
+ const ecdsaApi = client.ECDSA();
+
+ pkixPublicKeys.push(
+ await ecdsaApi.publicKey(masterKeyId, chainPath)
+ );
+ }
+
+ // Validate public keys
+
+ for (let i = 1; i < pkixPublicKeys.length; i++) {
+ if (Buffer.compare(pkixPublicKeys[0], pkixPublicKeys[i]) !== 0) {
+ throw Error("public keys do not match");
+ }
+ }
+
+ const pkixPublicKey = pkixPublicKeys[0];
+
+ // Convert the public key into an Ethereum address
+ const utils = clients[0].Utils();
+
+ const publicKeyBytes = await utils.pkixPublicKeyToUncompressedPoint(
+ pkixPublicKey
+ );
+
+ // Convert web3 publickey to address
+ var publicKeyHex = toHex(publicKeyBytes);
+ console.log("Public Key of derived key m/44/60:", publicKeyHex);
+
+ // Remove '0x' prefox
+ if (publicKeyHex.startsWith('0x')) {
+ publicKeyHex = publicKeyHex.slice(2);
+ }
+
+ // Remove the leading '04' byte (which signifies an uncompressed public key)
+ if (publicKeyHex.startsWith('04')) {
+ publicKeyHex = publicKeyHex.slice(2);
+ }
+
+ // Compute the keccak256 hash of the public key
+ const addressBuffer = keccak256(Buffer.from(publicKeyHex, 'hex'));
+
+ // Take the last 20 bytes of the hash, prefix it with '0x', and convert to string
+ const address = toChecksumAddress('0x' + addressBuffer.slice(-40));
+
+ console.log(`Ethereum address of derived key m/44/60: ${address}`);
+
+
+ const response = await createStakeIntent(process.env.BLOCKDAEMON_STAKE_API_KEY, {
+ stakes: [
+ {
+ amount: '32000000000',
+ withdrawal_address: process.env.ETHEREUM_WITHDRAWAL_ADDRESS,
+ fee_recipient: process.env.ETHEREUM_WITHDRAWAL_ADDRESS,
+ },
+ ],
+ });
+
+ const { unsigned_transaction, contract_address, stakes } = response.ethereum;
+ const totalDepositAmount =
+ stakes.reduce((sum, next) => sum + BigInt(next.amount), 0n) * gwei;
+
+ const web3 = new Web3(`https://svc.blockdaemon.com/ethereum/holesky/native?apiKey=${process.env.BLOCKDAEMON_API_KEY}`);
+
+ // log initial balances
+ console.log("Initial balance:", await web3.eth.getBalance(address));
+
+ // used to calculate the transaction's maxFeePerGas
+ const feeData = await web3.eth.calculateFeeData();
+
+ const transaction = FeeMarketEIP1559Transaction.fromTxData(
+ {
+ chainId: await web3.eth.getChainId(),
+ type: 2,
+ to: contract_address,
+ value: BigInt(totalDepositAmount.toString(10)),
+ data: web3.utils.hexToBytes(unsigned_transaction),
+ nonce: await web3.eth.getTransactionCount(address as string),
+ gasLimit: feeData.gasPrice,
+ maxFeePerGas: feeData.maxFeePerGas,
+ maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
+ }
+ );
+
+ const txHash = transaction.getMessageToSign(true);
+ console.log('Raw Transaction:', web3.utils.toHex(transaction.serialize()));
+ console.log('Transaction Hash:', web3.utils.toHex(txHash));
+ const {r,s,v} = await signTx(txHash, clients, masterKeyId, chainPath);
+
+ const signedTransaction = transaction._processSignature(v, web3.utils.hexToBytes(r), web3.utils.hexToBytes(s));
+
+ console.log('Get Sender Address:', signedTransaction.getSenderAddress().toString());
+ console.log('Get Sender pk:', web3.utils.bytesToHex(signedTransaction.getSenderPublicKey()));
+
+ const serializeTx = TransactionFactory.fromTxData(signedTransaction).serialize();
+ console.log('Signed Transaction:', web3.utils.toHex(serializeTx));
+ const txReceipt = await web3.eth.sendSignedTransaction(serializeTx)
+
+ console.log(txReceipt);
+}
+
+
+
+async function signTx(
+ messageToSign: Uint8Array,
+ clients: TSMClient[],
+ masterKeyId: string,
+ chainPath: Uint32Array
+ ): Promise {
+
+
+ console.log(`Builder Vault signing transaction hash...`);
+
+ const partialSignatures: string[] = [];
+
+ const sessionConfig = await SessionConfig.newStaticSessionConfig(
+ await SessionConfig.GenerateSessionID(),
+ clients.length
+ );
+
+ const partialSignaturePromises: Promise[] = [];
+
+ for (const [_, client] of clients.entries()) {
+ const func = async (): Promise => {
+ const ecdsaApi = client.ECDSA();
+
+ const partialSignResult = await ecdsaApi.sign(
+ sessionConfig,
+ masterKeyId,
+ chainPath,
+ messageToSign
+ );
+
+ partialSignatures.push(partialSignResult);
+ };
+
+ partialSignaturePromises.push(func());
+ }
+
+ await Promise.all(partialSignaturePromises);
+
+ const ecdsaApi = clients[0].ECDSA();
+
+ const signature = await ecdsaApi.finalizeSignature(
+ messageToSign,
+ partialSignatures
+ );
+
+ // Define ASN.1 structure for decoding
+ const ASN1Signature = asn1.define("Signature", function () {
+ this.seq().obj(
+ this.key("r").int(),
+ this.key("s").int()
+ );
+ });
+
+ const decodedSignature = ASN1Signature.decode(Buffer.from(signature.signature));
+
+ return {
+ r: "0x" + decodedSignature.r.toString(16),
+ s: "0x" + decodedSignature.s.toString(16),
+ v: BigInt(signature.recoveryID! + 27), // Type 2 transaction with ._processSignature subtracts 27 Post EIP-155 should be: chainId * 2 + 35 + signature.recoveryID;
+ };
+ }
+
+
+async function getKeyId(
+ clients: TSMClient[],
+ threshold: number,
+ keyfile: string
+ ): Promise {
+ if (fs.existsSync(keyfile)) {
+ const data = fs.readFileSync(keyfile).toString().trim();
+
+ console.log(`Read key with ID ${data} from file ${keyfile}`);
+
+ return data;
+ }
+
+ // ToDo: Change to newStaticSessionConfig once TSM nodes are publically signed
+
+ // The public keys of the other players to encrypt MPC protocol data end-to-end
+ const playerB64Pubkeys = [
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtDFBfanInAMHNKKDG2RW/DiSnYeI7scVvfHIwUIRdbPH0gBrsilqxlvsKZTakN8om/Psc6igO+224X8T0J9eMg==",
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqvSkhonTeNhlETse8v3X7g4p100EW9xIqg4aRpD8yDXgB0UYjhd+gFtOCsRT2lRhuqNForqqC+YnBsJeZ4ANxg==",
+ ];
+
+ const playerPubkeys = [];
+ const playerIds = new Uint32Array(Array(clients.length).fill(0).map((_, i) => i));
+ for (const i of playerIds) {
+ const pubkey = Buffer.from(playerB64Pubkeys[i], "base64");
+ playerPubkeys.push(pubkey);
+ }
+
+ const sessionConfig = await SessionConfig.newSessionConfig(await SessionConfig.GenerateSessionID(), playerIds, playerPubkeys);
+
+ // const sessionConfig = await SessionConfig.newStaticSessionConfig(
+ // await SessionConfig.GenerateSessionID(),
+ // clients.length
+ // );
+
+ const masterKeyIds: string[] = [];
+
+ clients.forEach(() => masterKeyIds.push(""));
+
+ const promises: Promise[] = [];
+
+ for (const [i, client] of clients.entries()) {
+ const func = async (): Promise => {
+ const ecdsaApi = client.ECDSA();
+
+ masterKeyIds[i] = await ecdsaApi.generateKey(
+ sessionConfig,
+ threshold,
+ curves.SECP256K1
+ );
+ };
+
+ promises.push(func());
+ }
+
+ await Promise.all(promises);
+
+ for (let i = 1; i < masterKeyIds.length; i++) {
+ if (masterKeyIds[0] !== masterKeyIds[i]) {
+ throw Error("Key ids do not match");
+ }
+ }
+
+ const keyID = masterKeyIds[0];
+
+ console.log(`Generated master key (m) with ID ${keyID} ; saving to file ${keyfile}`);
+
+ fs.writeFileSync(keyfile, `${keyID}\n`);
+
+ return keyID;
+}
+
+// run the example
+main()
+ .then(() => process.exit(0))
+ .catch(err => {
+ console.error(err);
+ process.exit(1);
+ });
diff --git a/ethereum-staking/buildervault-wallet/nodejs/package-lock.json b/ethereum-staking/buildervault-wallet/nodejs/package-lock.json
new file mode 100644
index 0000000..931db86
--- /dev/null
+++ b/ethereum-staking/buildervault-wallet/nodejs/package-lock.json
@@ -0,0 +1,870 @@
+{
+ "name": "nodejs",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "dependencies": {
+ "crypto": "^1.0.1",
+ "web3": "^4.11.1",
+ "web3-eth-accounts": "^4.1.3"
+ }
+ },
+ "node_modules/@adraffy/ens-normalize": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz",
+ "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw=="
+ },
+ "node_modules/@ethereumjs/rlp": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz",
+ "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==",
+ "bin": {
+ "rlp": "bin/rlp"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@noble/curves": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz",
+ "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==",
+ "dependencies": {
+ "@noble/hashes": "1.4.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@noble/hashes": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz",
+ "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@scure/base": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.7.tgz",
+ "integrity": "sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==",
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@scure/bip32": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz",
+ "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==",
+ "dependencies": {
+ "@noble/curves": "~1.4.0",
+ "@noble/hashes": "~1.4.0",
+ "@scure/base": "~1.1.6"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@scure/bip39": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz",
+ "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==",
+ "dependencies": {
+ "@noble/hashes": "~1.4.0",
+ "@scure/base": "~1.1.6"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.14.12",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.12.tgz",
+ "integrity": "sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/ws": {
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
+ "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/abitype": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/abitype/-/abitype-0.7.1.tgz",
+ "integrity": "sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ==",
+ "peerDependencies": {
+ "typescript": ">=4.9.4",
+ "zod": "^3 >=3.19.1"
+ },
+ "peerDependenciesMeta": {
+ "zod": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/crc-32": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+ "bin": {
+ "crc32": "bin/crc32.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/cross-fetch": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
+ "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
+ "dependencies": {
+ "node-fetch": "^2.6.12"
+ }
+ },
+ "node_modules/crypto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz",
+ "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==",
+ "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in."
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ethereum-cryptography": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz",
+ "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==",
+ "dependencies": {
+ "@noble/curves": "1.4.2",
+ "@noble/hashes": "1.4.0",
+ "@scure/bip32": "1.4.0",
+ "@scure/bip39": "1.3.0"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
+ },
+ "node_modules/for-each": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "dependencies": {
+ "is-callable": "^1.1.3"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/is-arguments": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+ "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+ "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz",
+ "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
+ "dependencies": {
+ "which-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isomorphic-ws": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz",
+ "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==",
+ "peerDependencies": {
+ "ws": "*"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
+ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "node_modules/typescript": {
+ "version": "5.5.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
+ "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ },
+ "node_modules/util": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+ "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "which-typed-array": "^1.1.2"
+ }
+ },
+ "node_modules/web3": {
+ "version": "4.11.1",
+ "resolved": "https://registry.npmjs.org/web3/-/web3-4.11.1.tgz",
+ "integrity": "sha512-KUntBtnc+cj9ur/yNcdTok9MpCI9dHf8h1hRmLPVICF5wyKyHbR4t+51vqUnK5bI6UxVfRPT++qCcP7KhDACVA==",
+ "dependencies": {
+ "web3-core": "^4.5.0",
+ "web3-errors": "^1.2.1",
+ "web3-eth": "^4.8.2",
+ "web3-eth-abi": "^4.2.3",
+ "web3-eth-accounts": "^4.1.3",
+ "web3-eth-contract": "^4.6.0",
+ "web3-eth-ens": "^4.4.0",
+ "web3-eth-iban": "^4.0.7",
+ "web3-eth-personal": "^4.0.8",
+ "web3-net": "^4.1.0",
+ "web3-providers-http": "^4.1.0",
+ "web3-providers-ws": "^4.0.8",
+ "web3-rpc-methods": "^1.3.0",
+ "web3-rpc-providers": "^1.0.0-rc.1",
+ "web3-types": "^1.7.0",
+ "web3-utils": "^4.3.1",
+ "web3-validator": "^2.0.6"
+ },
+ "engines": {
+ "node": ">=14.0.0",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-core": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-4.5.0.tgz",
+ "integrity": "sha512-Q8LIAqmF7vkRydBPiU+OC7wI44nEU6JEExolFaOakqrjMtQ1CWFHRUQMNJRDsk5bRirjyShuAsuqLeYByvvXhg==",
+ "dependencies": {
+ "web3-errors": "^1.2.0",
+ "web3-eth-accounts": "^4.1.2",
+ "web3-eth-iban": "^4.0.7",
+ "web3-providers-http": "^4.1.0",
+ "web3-providers-ws": "^4.0.7",
+ "web3-types": "^1.7.0",
+ "web3-utils": "^4.3.0",
+ "web3-validator": "^2.0.6"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ },
+ "optionalDependencies": {
+ "web3-providers-ipc": "^4.0.7"
+ }
+ },
+ "node_modules/web3-errors": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/web3-errors/-/web3-errors-1.2.1.tgz",
+ "integrity": "sha512-dIsi8SFC9TCAWpPmacXeVMk/F8tDNa1Bvg8/Cc2cvJo8LRSWd099szEyb+/SiMYcLlEbwftiT9Rpukz7ql4hBg==",
+ "dependencies": {
+ "web3-types": "^1.7.0"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-eth": {
+ "version": "4.8.2",
+ "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-4.8.2.tgz",
+ "integrity": "sha512-DLV/fIMG6gBp/B0gv0+G4FzxZ4YCDQsY3lzqqv7avwh3uU7/O27aifCUcFd7Ye+3ixTqCjAvLEl9wYSeyG3zQw==",
+ "dependencies": {
+ "setimmediate": "^1.0.5",
+ "web3-core": "^4.5.0",
+ "web3-errors": "^1.2.1",
+ "web3-eth-abi": "^4.2.3",
+ "web3-eth-accounts": "^4.1.3",
+ "web3-net": "^4.1.0",
+ "web3-providers-ws": "^4.0.8",
+ "web3-rpc-methods": "^1.3.0",
+ "web3-types": "^1.7.0",
+ "web3-utils": "^4.3.1",
+ "web3-validator": "^2.0.6"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-eth-abi": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-4.2.3.tgz",
+ "integrity": "sha512-rPVwTn0O1CzbtfXwEfIjUP0W5Y7u1OFjugwKpSqJzPQE6+REBg6OELjomTGZBu+GThxHnv0rp15SOxvqp+tyXA==",
+ "dependencies": {
+ "abitype": "0.7.1",
+ "web3-errors": "^1.2.0",
+ "web3-types": "^1.7.0",
+ "web3-utils": "^4.3.1",
+ "web3-validator": "^2.0.6"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-eth-accounts": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-4.1.3.tgz",
+ "integrity": "sha512-61Nb7xCXy6Vw/6xUZMM5ITtXetXmaP0F8oKRxika4GO4fRfKZLAwBZtshMyrdAORPZYq77ENiqXJVU+hTmtUaQ==",
+ "dependencies": {
+ "@ethereumjs/rlp": "^4.0.1",
+ "crc-32": "^1.2.2",
+ "ethereum-cryptography": "^2.0.0",
+ "web3-errors": "^1.2.0",
+ "web3-types": "^1.7.0",
+ "web3-utils": "^4.3.1",
+ "web3-validator": "^2.0.6"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-eth-contract": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-4.6.0.tgz",
+ "integrity": "sha512-mgQ/WUUlgW9BVKKVGU/Q7KrQEbEGI98h8ppox7fT964wY9ITFMDuRCvYk50WTWnFMdjFtOBqt1xRJ0+B1ekCHg==",
+ "dependencies": {
+ "@ethereumjs/rlp": "^5.0.2",
+ "web3-core": "^4.5.0",
+ "web3-errors": "^1.2.0",
+ "web3-eth": "^4.8.1",
+ "web3-eth-abi": "^4.2.3",
+ "web3-types": "^1.7.0",
+ "web3-utils": "^4.3.1",
+ "web3-validator": "^2.0.6"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-eth-contract/node_modules/@ethereumjs/rlp": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz",
+ "integrity": "sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==",
+ "bin": {
+ "rlp": "bin/rlp.cjs"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/web3-eth-ens": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-4.4.0.tgz",
+ "integrity": "sha512-DeyVIS060hNV9g8dnTx92syqvgbvPricE3MerCxe/DquNZT3tD8aVgFfq65GATtpCgDDJffO2bVeHp3XBemnSQ==",
+ "dependencies": {
+ "@adraffy/ens-normalize": "^1.8.8",
+ "web3-core": "^4.5.0",
+ "web3-errors": "^1.2.0",
+ "web3-eth": "^4.8.0",
+ "web3-eth-contract": "^4.5.0",
+ "web3-net": "^4.1.0",
+ "web3-types": "^1.7.0",
+ "web3-utils": "^4.3.0",
+ "web3-validator": "^2.0.6"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-eth-iban": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-4.0.7.tgz",
+ "integrity": "sha512-8weKLa9KuKRzibC87vNLdkinpUE30gn0IGY027F8doeJdcPUfsa4IlBgNC4k4HLBembBB2CTU0Kr/HAOqMeYVQ==",
+ "dependencies": {
+ "web3-errors": "^1.1.3",
+ "web3-types": "^1.3.0",
+ "web3-utils": "^4.0.7",
+ "web3-validator": "^2.0.3"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-eth-personal": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-4.0.8.tgz",
+ "integrity": "sha512-sXeyLKJ7ddQdMxz1BZkAwImjqh7OmKxhXoBNF3isDmD4QDpMIwv/t237S3q4Z0sZQamPa/pHebJRWVuvP8jZdw==",
+ "dependencies": {
+ "web3-core": "^4.3.0",
+ "web3-eth": "^4.3.1",
+ "web3-rpc-methods": "^1.1.3",
+ "web3-types": "^1.3.0",
+ "web3-utils": "^4.0.7",
+ "web3-validator": "^2.0.3"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-net": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-4.1.0.tgz",
+ "integrity": "sha512-WWmfvHVIXWEoBDWdgKNYKN8rAy6SgluZ0abyRyXOL3ESr7ym7pKWbfP4fjApIHlYTh8tNqkrdPfM4Dyi6CA0SA==",
+ "dependencies": {
+ "web3-core": "^4.4.0",
+ "web3-rpc-methods": "^1.3.0",
+ "web3-types": "^1.6.0",
+ "web3-utils": "^4.3.0"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-providers-http": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-4.1.0.tgz",
+ "integrity": "sha512-6qRUGAhJfVQM41E5t+re5IHYmb5hSaLc02BE2MaRQsz2xKA6RjmHpOA5h/+ojJxEpI9NI2CrfDKOAgtJfoUJQg==",
+ "dependencies": {
+ "cross-fetch": "^4.0.0",
+ "web3-errors": "^1.1.3",
+ "web3-types": "^1.3.0",
+ "web3-utils": "^4.0.7"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-providers-ipc": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-4.0.7.tgz",
+ "integrity": "sha512-YbNqY4zUvIaK2MHr1lQFE53/8t/ejHtJchrWn9zVbFMGXlTsOAbNoIoZWROrg1v+hCBvT2c9z8xt7e/+uz5p1g==",
+ "optional": true,
+ "dependencies": {
+ "web3-errors": "^1.1.3",
+ "web3-types": "^1.3.0",
+ "web3-utils": "^4.0.7"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-providers-ws": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-4.0.8.tgz",
+ "integrity": "sha512-goJdgata7v4pyzHRsg9fSegUG4gVnHZSHODhNnn6J93ykHkBI1nz4fjlGpcQLUMi4jAMz6SHl9Ibzs2jj9xqPw==",
+ "dependencies": {
+ "@types/ws": "8.5.3",
+ "isomorphic-ws": "^5.0.0",
+ "web3-errors": "^1.2.0",
+ "web3-types": "^1.7.0",
+ "web3-utils": "^4.3.1",
+ "ws": "^8.17.1"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-rpc-methods": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/web3-rpc-methods/-/web3-rpc-methods-1.3.0.tgz",
+ "integrity": "sha512-/CHmzGN+IYgdBOme7PdqzF+FNeMleefzqs0LVOduncSaqsppeOEoskLXb2anSpzmQAP3xZJPaTrkQPWSJMORig==",
+ "dependencies": {
+ "web3-core": "^4.4.0",
+ "web3-types": "^1.6.0",
+ "web3-validator": "^2.0.6"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-rpc-providers": {
+ "version": "1.0.0-rc.1",
+ "resolved": "https://registry.npmjs.org/web3-rpc-providers/-/web3-rpc-providers-1.0.0-rc.1.tgz",
+ "integrity": "sha512-N7AgGB+ilKPFQohnlI1vNHWmQ5Wh5vlGdYKWCWJc9kisKxxGtOsqN3W8tOj6/898sHZIXU9i/IAOyreGDIybmw==",
+ "dependencies": {
+ "web3-errors": "^1.2.0",
+ "web3-providers-http": "^4.1.0",
+ "web3-providers-ws": "^4.0.8",
+ "web3-types": "^1.7.0",
+ "web3-utils": "^4.3.1",
+ "web3-validator": "^2.0.6"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-types": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/web3-types/-/web3-types-1.7.0.tgz",
+ "integrity": "sha512-nhXxDJ7a5FesRw9UG5SZdP/C/3Q2EzHGnB39hkAV+YGXDMgwxBXFWebQLfEzZzuArfHnvC0sQqkIHNwSKcVjdA==",
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-utils": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-4.3.1.tgz",
+ "integrity": "sha512-kGwOk8FxOLJ9DQC68yqNQc7AzN+k9YDLaW+ZjlAXs3qORhf8zXk5SxWAAGLbLykMs3vTeB0FTb1Exut4JEYfFA==",
+ "dependencies": {
+ "ethereum-cryptography": "^2.0.0",
+ "eventemitter3": "^5.0.1",
+ "web3-errors": "^1.2.0",
+ "web3-types": "^1.7.0",
+ "web3-validator": "^2.0.6"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-validator": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/web3-validator/-/web3-validator-2.0.6.tgz",
+ "integrity": "sha512-qn9id0/l1bWmvH4XfnG/JtGKKwut2Vokl6YXP5Kfg424npysmtRLe9DgiNBM9Op7QL/aSiaA0TVXibuIuWcizg==",
+ "dependencies": {
+ "ethereum-cryptography": "^2.0.0",
+ "util": "^0.12.5",
+ "web3-errors": "^1.2.0",
+ "web3-types": "^1.6.0",
+ "zod": "^3.21.4"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
+ "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.23.8",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
+ "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ }
+ }
+}
diff --git a/ethereum-staking/buildervault-wallet/nodejs/package.json b/ethereum-staking/buildervault-wallet/nodejs/package.json
new file mode 100644
index 0000000..af5fdd6
--- /dev/null
+++ b/ethereum-staking/buildervault-wallet/nodejs/package.json
@@ -0,0 +1,7 @@
+{
+ "dependencies": {
+ "crypto": "^1.0.1",
+ "web3": "^4.11.1",
+ "web3-eth-accounts": "^4.1.3"
+ }
+}
diff --git a/solana-staking/buildervault-wallet/nodejs/.env.example b/solana-staking/buildervault-wallet/nodejs/.env.example
new file mode 100644
index 0000000..40cd012
--- /dev/null
+++ b/solana-staking/buildervault-wallet/nodejs/.env.example
@@ -0,0 +1,6 @@
+# Blockdaemon Stake
+SOLANA_VALIDATOR_ADDRESS=HiFjzpR7e5Kv2tdU9jtE4FbH1X8Z9Syia3Uadadx18b5 # Blockdaemon Solana Testnet vote account = HiFjzpR7e5Kv2tdU9jtE4FbH1X8Z9Syia3Uadadx18b5
+BLOCKDAEMON_API_KEY=
+BLOCKDAEMON_STAKE_API_KEY=
+SOLANA_NETWORK=testnet # mainnet | testnet | devnet
+PLAN_ID= # Optional. If provided, will use a specific validator plan.
diff --git a/solana-staking/buildervault-wallet/nodejs/README.md b/solana-staking/buildervault-wallet/nodejs/README.md
new file mode 100644
index 0000000..79c52a0
--- /dev/null
+++ b/solana-staking/buildervault-wallet/nodejs/README.md
@@ -0,0 +1,70 @@
+
+# Solana staking on Testnet with Builder Vault wallet
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant StakeClient as Sample stake
client application
+ participant StakeAPI as Stake Intent API
+ participant Blockchain as Solana RPC
+ box Builder Vault
+ participant TSM1 as MPC Wallet
(private key share 1)
+ participant TSM2 as MPC Wallet
(private key share 2)
+ end
+
+ StakeClient ->> StakeAPI: get StakeIntent unsigned tx data
(amount, validator vote account, delegator)
+ StakeClient ->> StakeClient: construct unsigned tx
+ StakeClient ->> Blockchain: get blockchain inputs (gas fee) for new tx
(sender wallet)
+ StakeClient ->> Blockchain: check delegator account balance for stake
(sender wallet)
+ StakeClient ->> TSM1: request signature (unsigned tx hash)
+ TSM1 -->> StakeClient: return partial signature
+ StakeClient ->> TSM2: request signature (unsigned tx hash)
+ TSM2 -->> StakeClient: return partial signature
+ StakeClient ->> StakeClient: combine partial signatures
add signature to tx
verify tx signatures
+ StakeClient ->> Blockchain: broadcast full signed tx
(signed tx)
+```
+
+### Prerequisites
+ - [Node.js](https://nodejs.org/en/download/package-manager) or use [code-spaces](https://codespaces.new/Blockdaemon/demo-buildervault-stakingAPI?quickstart=1)
+ - Register for a demo Builder Vault tenant: https://www.blockdaemon.com/get-started/builder-vault-sandbox-registration
+ - Download SDK bundle provided in registration email (extract authentication certificates)
+ - Place Builder Vault authentication certificate key-pair `client.crt` & `client.key` in this nodejs folder
+ - Register for free Blockdaemon [RPC API key](https://docs.blockdaemon.com/reference/get-started-rpc#step-1-sign-up-for-an-api-key) and set in .env as BLOCKDAEMON_API_KEY
+ - Register for free Blockdaemon [Staking API key](https://docs.blockdaemon.com/reference/get-started-staking-api#step-1-sign-up-for-an-api-key) and set in .env as BLOCKDAEMON_STAKE_API_KEY
+ - Speak to your CSM about getting credentials to the Blockdaemon nexus.sepior.net repo for the nodejs SDK.
+
+### Step 1. Set environment variables in .env
+```shell
+cd solana-staking/buildervault-wallet/nodejs/
+cp .env.example .env
+```
+- update .env with API keys
+
+### Step 2. Install package dependancies
+- replace NEXUS_USERNAME & NEXUS_PASSWORD with credential provided by your CSM
+```shell
+npm config set @sepior:registry=https://nexus.sepior.net/repository/sepior-nodejs-tsm-sdk-group/
+npm config set //nexus.sepior.net/repository/sepior-nodejs-tsm-sdk-group/:username=NEXUS_USERNAME
+npm config set //nexus.sepior.net/repository/sepior-nodejs-tsm-sdk-group/:\_password=`echo -n 'NEXUS_PASSWORD' | base64`
+npm install
+```
+
+### Step 3. Launch solana-stake-bv.ts to auto-create the Builder Vault wallet address on first run
+```shell
+npm run start solana-stake-bv.ts
+```
+- note, on first run this step will fail as the wallet address has no funds
+- copy the new Solana wallet address and fund the account
+
+### Step 4. Fund the new Solana wallet address with 2 SOL using faucets below
+ - https://solfaucet.com
+ - https://faucet.triangleplatform.com/solana/testnet
+ - https://faucet.quicknode.com/solana/testnet
+ - https://solfate.com/faucet
+
+### Step 5. Launch solana-stake-bv.ts to generate the Stake Intent request, sign the request with BuilderVault and broadcast the transaction
+```shell
+npm run start solana-stake-bv.ts
+```
+- [optional] view the signed transaction contents with inspector: https://explorer.solana.com/tx/inspector?cluster=testnet
+- observe the confirmed transaction through the generated blockexplorer link
diff --git a/solana-staking/buildervault-wallet/nodejs/package-lock.json b/solana-staking/buildervault-wallet/nodejs/package-lock.json
new file mode 100644
index 0000000..56d339c
--- /dev/null
+++ b/solana-staking/buildervault-wallet/nodejs/package-lock.json
@@ -0,0 +1,947 @@
+{
+ "name": "nodejs",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "devDependencies": {
+ "@sepior/tsmsdkv2": "^63.0.0",
+ "@solana/web3.js": "^1.95.1",
+ "@types/node": "^20.14.12",
+ "bs58": "^6.0.0",
+ "dotenv": "^16.4.5",
+ "ts-node": "^10.9.2"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.24.8",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz",
+ "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==",
+ "dev": true,
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "node_modules/@noble/curves": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz",
+ "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==",
+ "dev": true,
+ "dependencies": {
+ "@noble/hashes": "1.4.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@noble/hashes": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz",
+ "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@sepior/tsmsdkv2": {
+ "version": "63.0.0",
+ "resolved": "https://nexus.sepior.net/repository/sepior-nodejs-tsm-sdk-group/@sepior/tsmsdkv2/-/tsmsdkv2-63.0.0.tgz",
+ "integrity": "sha512-SERPces5/gCumUzZK8fE0oGWG6+zlRohz1VWLjEzncWEAWeC65RNhIQvCrCUSosirpoSSflxx5Kf7OHU19M8UA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "bindings": "^1.5.0",
+ "elliptic": "^6.5.4",
+ "node-addon-api": "^7.1.0"
+ }
+ },
+ "node_modules/@solana/buffer-layout": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz",
+ "integrity": "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==",
+ "dev": true,
+ "dependencies": {
+ "buffer": "~6.0.3"
+ },
+ "engines": {
+ "node": ">=5.10"
+ }
+ },
+ "node_modules/@solana/web3.js": {
+ "version": "1.95.1",
+ "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.95.1.tgz",
+ "integrity": "sha512-mRX/AjV6QbiOXpWcy5Rz1ZWEH2lVkwO7T0pcv9t97ACpv3/i3tPiqXwk0JIZgSR3wOSTiT26JfygnJH2ulS6dQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.24.8",
+ "@noble/curves": "^1.4.2",
+ "@noble/hashes": "^1.4.0",
+ "@solana/buffer-layout": "^4.0.1",
+ "agentkeepalive": "^4.5.0",
+ "bigint-buffer": "^1.1.5",
+ "bn.js": "^5.2.1",
+ "borsh": "^0.7.0",
+ "bs58": "^4.0.1",
+ "buffer": "6.0.3",
+ "fast-stable-stringify": "^1.0.0",
+ "jayson": "^4.1.1",
+ "node-fetch": "^2.7.0",
+ "rpc-websockets": "^9.0.2",
+ "superstruct": "^2.0.2"
+ }
+ },
+ "node_modules/@solana/web3.js/node_modules/base-x": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz",
+ "integrity": "sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/@solana/web3.js/node_modules/bs58": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
+ "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==",
+ "dev": true,
+ "dependencies": {
+ "base-x": "^3.0.2"
+ }
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.12",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.12.tgz",
+ "integrity": "sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
+ "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
+ "dev": true
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.38",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.14.12",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.12.tgz",
+ "integrity": "sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==",
+ "dev": true,
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/uuid": {
+ "version": "8.3.4",
+ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
+ "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
+ "dev": true
+ },
+ "node_modules/@types/ws": {
+ "version": "7.4.7",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz",
+ "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.12.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
+ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.3.3",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz",
+ "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/agentkeepalive": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
+ "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
+ "dev": true,
+ "dependencies": {
+ "humanize-ms": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ }
+ },
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
+ "node_modules/base-x": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz",
+ "integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==",
+ "dev": true
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/bigint-buffer": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz",
+ "integrity": "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "bindings": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dev": true,
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "node_modules/bn.js": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
+ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==",
+ "dev": true
+ },
+ "node_modules/borsh": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz",
+ "integrity": "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==",
+ "dev": true,
+ "dependencies": {
+ "bn.js": "^5.2.0",
+ "bs58": "^4.0.0",
+ "text-encoding-utf-8": "^1.0.2"
+ }
+ },
+ "node_modules/borsh/node_modules/base-x": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz",
+ "integrity": "sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/borsh/node_modules/bs58": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
+ "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==",
+ "dev": true,
+ "dependencies": {
+ "base-x": "^3.0.2"
+ }
+ },
+ "node_modules/brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
+ "dev": true
+ },
+ "node_modules/bs58": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz",
+ "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==",
+ "dev": true,
+ "dependencies": {
+ "base-x": "^5.0.0"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/bufferutil": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz",
+ "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "dependencies": {
+ "node-gyp-build": "^4.3.0"
+ },
+ "engines": {
+ "node": ">=6.14.2"
+ }
+ },
+ "node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true
+ },
+ "node_modules/delay": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz",
+ "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/elliptic": {
+ "version": "6.5.6",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.6.tgz",
+ "integrity": "sha512-mpzdtpeCLuS3BmE3pO3Cpp5bbjlOPY2Q0PgoF+Od1XZrHLYI28Xe3ossCmYCQt11FQKEYd9+PF8jymTvtWJSHQ==",
+ "dev": true,
+ "dependencies": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/elliptic/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ },
+ "node_modules/es6-promise": {
+ "version": "4.2.8",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+ "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
+ "dev": true
+ },
+ "node_modules/es6-promisify": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+ "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==",
+ "dev": true,
+ "dependencies": {
+ "es6-promise": "^4.0.3"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "dev": true
+ },
+ "node_modules/eyes": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
+ "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==",
+ "dev": true,
+ "engines": {
+ "node": "> 0.1.90"
+ }
+ },
+ "node_modules/fast-stable-stringify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz",
+ "integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==",
+ "dev": true
+ },
+ "node_modules/file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "dev": true
+ },
+ "node_modules/hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "node_modules/hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
+ "dev": true,
+ "dependencies": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/humanize-ms": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+ "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.0.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/isomorphic-ws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
+ "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==",
+ "dev": true,
+ "peerDependencies": {
+ "ws": "*"
+ }
+ },
+ "node_modules/jayson": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.1.tgz",
+ "integrity": "sha512-5ZWm4Q/0DHPyeMfAsrwViwUS2DMVsQgWh8bEEIVTkfb3DzHZ2L3G5WUnF+AKmGjjM9r1uAv73SaqC1/U4RL45w==",
+ "dev": true,
+ "dependencies": {
+ "@types/connect": "^3.4.33",
+ "@types/node": "^12.12.54",
+ "@types/ws": "^7.4.4",
+ "commander": "^2.20.3",
+ "delay": "^5.0.0",
+ "es6-promisify": "^5.0.0",
+ "eyes": "^0.1.8",
+ "isomorphic-ws": "^4.0.1",
+ "json-stringify-safe": "^5.0.1",
+ "JSONStream": "^1.3.5",
+ "uuid": "^8.3.2",
+ "ws": "^7.5.10"
+ },
+ "bin": {
+ "jayson": "bin/jayson.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jayson/node_modules/@types/node": {
+ "version": "12.20.55",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
+ "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==",
+ "dev": true
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true
+ },
+ "node_modules/jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+ "dev": true,
+ "engines": [
+ "node >= 0.2.0"
+ ]
+ },
+ "node_modules/JSONStream": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+ "dev": true,
+ "dependencies": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ },
+ "bin": {
+ "JSONStream": "bin.js"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "node_modules/minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==",
+ "dev": true
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/node-addon-api": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
+ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+ "dev": true
+ },
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "dev": true,
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/node-gyp-build": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz",
+ "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==",
+ "dev": true,
+ "optional": true,
+ "bin": {
+ "node-gyp-build": "bin.js",
+ "node-gyp-build-optional": "optional.js",
+ "node-gyp-build-test": "build-test.js"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+ "dev": true
+ },
+ "node_modules/rpc-websockets": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-9.0.2.tgz",
+ "integrity": "sha512-YzggvfItxMY3Lwuax5rC18inhbjJv9Py7JXRHxTIi94JOLrqBsSsUUc5bbl5W6c11tXhdfpDPK0KzBhoGe8jjw==",
+ "dev": true,
+ "dependencies": {
+ "@swc/helpers": "^0.5.11",
+ "@types/uuid": "^8.3.4",
+ "@types/ws": "^8.2.2",
+ "buffer": "^6.0.3",
+ "eventemitter3": "^5.0.1",
+ "uuid": "^8.3.2",
+ "ws": "^8.5.0"
+ },
+ "funding": {
+ "type": "paypal",
+ "url": "https://paypal.me/kozjak"
+ },
+ "optionalDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ }
+ },
+ "node_modules/rpc-websockets/node_modules/@types/ws": {
+ "version": "8.5.11",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz",
+ "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/rpc-websockets/node_modules/ws": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/superstruct": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz",
+ "integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/text-encoding-utf-8": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz",
+ "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==",
+ "dev": true
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true
+ },
+ "node_modules/ts-node": {
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+ "dev": true,
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
+ "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
+ "dev": true
+ },
+ "node_modules/typescript": {
+ "version": "5.5.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
+ "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true
+ },
+ "node_modules/utf-8-validate": {
+ "version": "5.0.10",
+ "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
+ "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "dependencies": {
+ "node-gyp-build": "^4.3.0"
+ },
+ "engines": {
+ "node": ">=6.14.2"
+ }
+ },
+ "node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "dev": true
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "dev": true
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dev": true,
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/ws": {
+ "version": "7.5.10",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+ "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ }
+ }
+}
diff --git a/solana-staking/buildervault-wallet/nodejs/package.json b/solana-staking/buildervault-wallet/nodejs/package.json
new file mode 100644
index 0000000..b1baff3
--- /dev/null
+++ b/solana-staking/buildervault-wallet/nodejs/package.json
@@ -0,0 +1,15 @@
+{
+ "devDependencies": {
+ "@sepior/tsmsdkv2": "^63.0.0",
+ "@solana/web3.js": "^1.95.1",
+ "@types/node": "^20.14.12",
+ "bs58": "^6.0.0",
+ "dotenv": "^16.4.5",
+ "ts-node": "^10.9.2"
+ },
+ "type": "module",
+ "scripts": {
+ "start": "node --no-warnings=ExperimentalWarning --loader ts-node/esm"
+ },
+ "exclude": ["node_modules"]
+}
\ No newline at end of file
diff --git a/solana-staking/buildervault-wallet/nodejs/solana-stake-bv.ts b/solana-staking/buildervault-wallet/nodejs/solana-stake-bv.ts
new file mode 100644
index 0000000..08c7eb5
--- /dev/null
+++ b/solana-staking/buildervault-wallet/nodejs/solana-stake-bv.ts
@@ -0,0 +1,356 @@
+// @ts-ignore
+import { TSMClient, Configuration, SessionConfig, curves } from "@sepior/tsmsdkv2";
+import web3 from '@solana/web3.js';
+import bs58 from 'bs58';
+import fs from "fs";
+import crypto from "crypto";
+import 'dotenv/config'
+
+export type StakeIntentSolanaRequest = {
+ amount: string;
+ validator_address: string;
+ delegator_address: string;
+ staking_authority?: string;
+ withdrawal_authority?: string;
+ plan_id?: string;
+};
+
+export type StakeIntentSolana = {
+ stake_id: string;
+ amount: string;
+ validator_public_key: string;
+ staking_authority: string;
+ withdrawal_authority: string;
+ stake_account_public_key: string;
+ unsigned_transaction: string;
+};
+
+export type StakeIntentResponce = {
+ stake_intent_id: string;
+ protocol: string;
+ network: string;
+ solana?: StakeIntentSolana;
+ customer_id?: string;
+};
+
+async function main() {
+
+ const amountLamports = Math.floor(web3.LAMPORTS_PER_SOL * 1); // default 1 SOL
+
+ // Check for the required environment variables
+ if (!process.env.BLOCKDAEMON_API_KEY) {
+ throw new Error('BLOCKDAEMON_API_KEY environment variable not set');
+ }
+
+ if (!process.env.BLOCKDAEMON_STAKE_API_KEY) {
+ throw new Error('BLOCKDAEMON_STAKE_API_KEY environment variable not set');
+ }
+
+ if (!process.env.SOLANA_NETWORK) {
+ throw new Error('SOLANA_NETWORK environment variable not set.');
+ }
+
+ if (!process.env.SOLANA_VALIDATOR_ADDRESS) {
+ throw new Error('SOLANA_VALIDATOR_ADDRESS environment variable not set');
+ }
+
+ // Set buildervault endpoints
+
+ const serverMtlsPublicKeys = {
+ 0: `-----BEGIN CERTIFICATE-----\nMIICMTCCAdegAwIBAgICB+MwCgYIKoZIzj0EAwIwgaAxCzAJBgNVBAYTAlVTMRMw\nEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYDVQQHDAtMb3MgQW5nZWxlczEUMBIGA1UE\nCgwLQmxvY2tkYWVtb24xFDASBgNVBAsMC0Jsb2NrZGFlbW9uMRQwEgYDVQQDDAtC\nbG9ja2RhZW1vbjEkMCIGCSqGSIb3DQEJARYVYWRtaW5AYmxvY2tkYWVtb24uY29t\nMB4XDTI0MDIxMzE3MjE0OFoXDTI5MDIxMzE3MjE0OFowTjELMAkGA1UEBhMCVVMx\nEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC0xvcyBBbmdlbGVzMRQwEgYD\nVQQKEwtCbG9ja2RhZW1vbjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGlixcUc\nYC0ByeutoHHdi3zxWCg5iPAJcxVLvzBUdD2+XdCWEgS/xwFEef9Tl3xFdfK4iWSQ\nnjmtYMTaHMM6mfWjUjBQMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggrBgEF\nBQcDAgYIKwYBBQUHAwEwHwYDVR0jBBgwFoAUW6ouasv5oWo7MZ4ZzlE/mpbDrIMw\nCgYIKoZIzj0EAwIDSAAwRQIgSDKHZmsnylzL8kopFSeo8L6LQGxyd/NsBRb+8STI\n1cECIQChi4cl5nJgTXCBzJEHicnRk/0vl+9zq6iABMV+KTXJxA==\n-----END CERTIFICATE-----`,
+ 1: `-----BEGIN CERTIFICATE-----\nMIICMjCCAdegAwIBAgICB+MwCgYIKoZIzj0EAwIwgaAxCzAJBgNVBAYTAlVTMRMw\nEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYDVQQHDAtMb3MgQW5nZWxlczEUMBIGA1UE\nCgwLQmxvY2tkYWVtb24xFDASBgNVBAsMC0Jsb2NrZGFlbW9uMRQwEgYDVQQDDAtC\nbG9ja2RhZW1vbjEkMCIGCSqGSIb3DQEJARYVYWRtaW5AYmxvY2tkYWVtb24uY29t\nMB4XDTI0MDIxMzE3MjEzMloXDTI5MDIxMzE3MjEzMlowTjELMAkGA1UEBhMCVVMx\nEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC0xvcyBBbmdlbGVzMRQwEgYD\nVQQKEwtCbG9ja2RhZW1vbjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKz8yGcE\nYIhaQYCA2As30cRIL2rLrB2uKpcFpydE55RoI3Hw+QaeNCfR5znZQZM4bVVquT4i\nxDGhVnQKU5EQU/WjUjBQMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggrBgEF\nBQcDAgYIKwYBBQUHAwEwHwYDVR0jBBgwFoAUW6ouasv5oWo7MZ4ZzlE/mpbDrIMw\nCgYIKoZIzj0EAwIDSQAwRgIhAO9yXpssqar6IdgmEOIfAsha0ZIWG56nwE8/GbyN\nBiTaAiEAhhEClrSm/TzmWxODXamBz0pmQ9qNFsrtbGsDhLOe8O8=\n-----END CERTIFICATE-----`,
+ };
+
+ const cert0 = new crypto.X509Certificate(serverMtlsPublicKeys[0]);
+ const cert1 = new crypto.X509Certificate(serverMtlsPublicKeys[1]);
+
+ const config0 = await new Configuration("https://tsm-sandbox.prd.wallet.blockdaemon.app:8080");
+ await config0.withMTLSAuthentication("./client.key", "./client.crt", cert0.publicKey.export({type: "spki",format: "der"}));
+
+ const config1 = await new Configuration("https://tsm-sandbox.prd.wallet.blockdaemon.app:8081")
+ await config1.withMTLSAuthentication("./client.key", "./client.crt", cert1.publicKey.export({type: "spki",format: "der"}));
+
+ // Create clients for two MPC nodes
+ const clients: TSMClient[] = [
+ await TSMClient.withConfiguration(config0),
+ await TSMClient.withConfiguration(config1),
+ ];
+
+ const threshold = 1; // The security threshold for this key
+
+ const masterKeyId = await getKeyId(clients, threshold, "key.txt");
+
+ // Get the public key for the derived key m/44/501
+
+ const chainPath = new Uint32Array([44, 501]);
+
+ const publickeys: Uint8Array[] = [];
+
+ for (const [_, client] of clients.entries()) {
+ const eddsaApi = client.Schnorr();
+
+ publickeys.push(
+ await eddsaApi.publicKey(masterKeyId, chainPath)
+ );
+ }
+
+ // Validate public keys
+
+ for (let i = 1; i < publickeys.length; i++) {
+ if (Buffer.compare(publickeys[0], publickeys[i]) !== 0) {
+ throw Error("public keys do not match");
+ }
+ }
+
+
+ // Convert the Ed25519 public key to Base58 Solana address
+
+ const compressedPublicKey = await clients[0].Utils().pkixPublicKeyToCompressedPoint(publickeys[0]);
+ const delegatorAddress = new web3.PublicKey(bs58.encode(compressedPublicKey));
+ console.log(`Solana address of derived key m/44/501: ${delegatorAddress}\n`);
+
+
+ //const connection = new web3.Connection(`https://svc.blockdaemon.com/solana/${process.env.SOLANA_NETWORK}/native?apiKey=${process.env.BLOCKDAEMON_API_KEY}`, "confirmed");
+ const connection = new web3.Connection(`https://api.${process.env.SOLANA_NETWORK}.solana.com`, "confirmed"); // Todo: change to svc.blockdaemon.com when websockets is supported
+
+ // Check if validator exists
+ const voteAccounts = await connection.getVoteAccounts('finalized');
+ const found = voteAccounts.current.find(acc => acc.votePubkey.toString() == process.env.SOLANA_VALIDATOR_ADDRESS);
+ if(!found) {
+ throw "Validator address is not part of the active validators in the network";
+ }
+
+ // Create a stake intent with the Staking Integration API
+
+ const response = await createStakeIntent(process.env.BLOCKDAEMON_STAKE_API_KEY, {
+ amount: amountLamports.toString(),
+ validator_address: process.env.SOLANA_VALIDATOR_ADDRESS,
+ // By default `staking_authority` and `withdrawal_authority` will be
+ // the same as delegator address
+ delegator_address: delegatorAddress.toString(),
+ // Todo: add Plan-ID
+ });
+ if (!response.solana) {
+ throw "Missing property `solana` in BOSS responce";
+ }
+
+ // Get the unsigned transaction data returned by the Staking Integration API
+
+ const unsigned_transaction = response.solana.unsigned_transaction;
+ const transaction = web3.Transaction.from(Buffer.from(unsigned_transaction, 'hex'));
+
+ // Check the balance of the delegator
+
+ const delegatorBalance = await connection.getBalance(delegatorAddress);
+ const fee = await transaction.getEstimatedFee(connection);
+ if (fee === null) { throw new Error('Failed to estimate fee'); }
+ const delegatedAmount = Number(response.solana.amount);
+ if (delegatorBalance < delegatedAmount + fee) { throw `Insufficient funds: ${delegatorAddress} Balance: ${delegatorBalance}, Required: ${delegatedAmount + fee}` }
+
+
+ // Sign the transaction
+
+ console.log(`Transaction hash to sign base64: ${transaction.serializeMessage().toString("base64")}\n`);
+ const signature = await signTx(transaction.serializeMessage(),clients, masterKeyId, chainPath);
+ if (!signature) { throw new Error('Failed to sign transaction'); }
+
+ transaction.addSignature(delegatorAddress,signature);
+ if (!transaction.verifySignatures()) { throw new Error('Failed to verify signatures'); }
+
+ // Broadcast the transaction
+
+ console.log(`Full signed transaction base64: ${transaction.serialize().toString("base64")}\n`);
+ const latestBlockhash = await connection.getLatestBlockhash();
+ const txid = await web3.sendAndConfirmRawTransaction(connection, transaction.serialize(),
+ {
+ signature: bs58.encode(signature),
+ blockhash: latestBlockhash.blockhash,
+ lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
+ })
+
+ console.log(`Confirmed transaction: https://explorer.solana.com/tx/${txid}/?cluster=${process.env.SOLANA_NETWORK}`)
+}
+
+
+// Function for creating a stake intent with the Staking Integration API
+function createStakeIntent(
+ bossApiKey: string,
+ request: StakeIntentSolanaRequest,
+): Promise {
+ const requestOptions = {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ 'X-API-Key': bossApiKey,
+ //'Idempotency-Key': '1CAB9C75-4F4D-446C-B492-0198187C228',
+ },
+ body: JSON.stringify(request),
+ };
+
+ // return the response from POST Create a New Stake Intent
+ return fetch(
+ `https://svc.blockdaemon.com/boss/v1/solana/${process.env.SOLANA_NETWORK}/stake-intents`,
+ requestOptions
+ ).then(async response => {
+ if (response.status != 200) {
+ throw await response.json();
+ }
+ return response.json() as Promise
+ })
+}
+
+
+async function signTx(
+ messageToSign: Uint8Array,
+ clients: TSMClient[],
+ masterKeyId: string,
+ chainPath: Uint32Array
+): Promise {
+
+
+ console.log(`Builder Vault signing transaction hash...`);
+
+ const partialSignatures: string[] = [];
+
+ // ToDo: Change to newStaticSessionConfig once TSM nodes are publically signed
+
+ // The public keys of the other players to encrypt MPC protocol data end-to-end
+ const playerB64Pubkeys = [
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtDFBfanInAMHNKKDG2RW/DiSnYeI7scVvfHIwUIRdbPH0gBrsilqxlvsKZTakN8om/Psc6igO+224X8T0J9eMg==",
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqvSkhonTeNhlETse8v3X7g4p100EW9xIqg4aRpD8yDXgB0UYjhd+gFtOCsRT2lRhuqNForqqC+YnBsJeZ4ANxg==",
+ ];
+
+ const playerPubkeys = [];
+ const playerIds = new Uint32Array(Array(clients.length).fill(0).map((_, i) => i));
+ for (const i of playerIds) {
+ const pubkey = Buffer.from(playerB64Pubkeys[i], "base64");
+ playerPubkeys.push(pubkey);
+ }
+
+ const sessionConfig = await SessionConfig.newSessionConfig(await SessionConfig.GenerateSessionID(), playerIds, playerPubkeys);
+
+ // const sessionConfig = await SessionConfig.newStaticSessionConfig(
+ // await SessionConfig.GenerateSessionID(),
+ // clients.length
+ // );
+
+ const partialSignaturePromises: Promise[] = [];
+
+ for (const [_, client] of clients.entries()) {
+ const func = async (): Promise => {
+ const eddsaApi = client.Schnorr();
+
+ const partialSignResult = await eddsaApi.sign(
+ sessionConfig,
+ masterKeyId,
+ chainPath,
+ messageToSign
+ );
+
+ partialSignatures.push(partialSignResult);
+ };
+
+ partialSignaturePromises.push(func());
+ }
+
+ await Promise.all(partialSignaturePromises);
+
+ const eddsaApi = clients[0].Schnorr();
+
+ const signature = await eddsaApi.finalizeSignature(
+ messageToSign,
+ partialSignatures
+ );
+
+ // Verify the signature relative to the signed message and the public key
+
+ // try {
+ // const result = await eddsaApi.verifySignature(
+ // publickeys[0],
+ // messageToSign,
+ // signature.signature
+ // );
+ // console.log(
+ // `Signature: ${Buffer.from(signature.signature).toString("hex")}`
+ // );
+ // } catch (e) {
+ // console.log(e);
+ // }
+ return signature.signature;
+}
+
+async function getKeyId(
+ clients: TSMClient[],
+ threshold: number,
+ keyfile: string
+): Promise {
+ if (fs.existsSync(keyfile)) {
+ const data = fs.readFileSync(keyfile).toString().trim();
+
+ console.log(`Read key with ID ${data} from file ${keyfile}`);
+
+ return data;
+ }
+
+ // ToDo: Change to newStaticSessionConfig once TSM nodes are publically signed
+
+ // The public keys of the other players to encrypt MPC protocol data end-to-end
+ const playerB64Pubkeys = [
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtDFBfanInAMHNKKDG2RW/DiSnYeI7scVvfHIwUIRdbPH0gBrsilqxlvsKZTakN8om/Psc6igO+224X8T0J9eMg==",
+ "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqvSkhonTeNhlETse8v3X7g4p100EW9xIqg4aRpD8yDXgB0UYjhd+gFtOCsRT2lRhuqNForqqC+YnBsJeZ4ANxg==",
+ ];
+
+ const playerPubkeys = [];
+ const playerIds = new Uint32Array(Array(clients.length).fill(0).map((_, i) => i));
+ for (const i of playerIds) {
+ const pubkey = Buffer.from(playerB64Pubkeys[i], "base64");
+ playerPubkeys.push(pubkey);
+ }
+
+ const sessionConfig = await SessionConfig.newSessionConfig(await SessionConfig.GenerateSessionID(), playerIds, playerPubkeys);
+
+ // const sessionConfig = await SessionConfig.newStaticSessionConfig(
+ // await SessionConfig.GenerateSessionID(),
+ // clients.length
+ // );
+
+ const masterKeyIds: string[] = [];
+
+ clients.forEach(() => masterKeyIds.push(""));
+
+ const promises: Promise[] = [];
+
+ for (const [i, client] of clients.entries()) {
+ const func = async (): Promise => {
+ const eddsaApi = client.Schnorr();
+
+ masterKeyIds[i] = await eddsaApi.generateKey(sessionConfig, threshold, curves.ED25519,"");
+ };
+
+ promises.push(func());
+ }
+
+ await Promise.all(promises);
+
+ for (let i = 1; i < masterKeyIds.length; i++) {
+ if (masterKeyIds[0] !== masterKeyIds[i]) {
+ throw Error("Key ids do not match");
+ }
+ }
+
+ const keyID = masterKeyIds[0];
+
+ console.log(`Generated master key (m) with ID ${keyID} ; saving to file ${keyfile}`);
+
+ fs.writeFileSync(keyfile, `${keyID}\n`);
+
+ return keyID;
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch(err => {
+ console.error(err);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/solana-staking/buildervault-wallet/nodejs/tsconfig.json b/solana-staking/buildervault-wallet/nodejs/tsconfig.json
new file mode 100644
index 0000000..800afe1
--- /dev/null
+++ b/solana-staking/buildervault-wallet/nodejs/tsconfig.json
@@ -0,0 +1,116 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+ /* Language and Environment */
+ "target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ "module": "esnext", /* Specify what module code is generated. */
+ // "rootDir": "./", /* Specify the root folder within your source files. */
+ "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ // "outDir": "./", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ // "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ },
+ // "ts-node": {
+ // "esm": true,
+ // "files": true,
+ // "transpileOnly": true,
+ // "experimentalResolver": true,
+ // "experimentalSpecifierResolution": "node"
+ // }
+ }
\ No newline at end of file