Skip to content

Commit 16269ce

Browse files
committed
feat(SOLNENG-27): add solana buildervault nodejs staking example
1 parent 05f238e commit 16269ce

File tree

18 files changed

+2914
-60
lines changed

18 files changed

+2914
-60
lines changed

.devcontainer/devcontainer.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"image": "mcr.microsoft.com/devcontainers/universal:2.11.2",
3+
//"forwardPorts": [3000],
4+
"customizations": {
5+
// Configure properties specific to VS Code.
6+
"vscode": {
7+
// Add the IDs of extensions you want installed when the container is created.
8+
"extensions": [
9+
"markdown.showPreview",
10+
"bierner.markdown-mermaid",
11+
"aaron-bond.better-comments",
12+
"ms-vscode.go"
13+
]
14+
}
15+
}
16+
}

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
*.crt
22
*.key
33
.vscode
4-
init
4+
init
5+
**/key.txt
6+
**/.env
7+
**/node_modules

README.md

+8-54
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,12 @@
1+
# Blockdaemon Staking API demos using different wallets and SDKs
12

2-
## Sample go staking app demo
3+
This repository contains multiple applications. Below are the links to the individual application folders and their README files.
4+
To get started, click on the links for installation and usage instructions.
35

6+
## Solana Staking
7+
- [Stake deposit from Builder Vault wallet with TypeScript SDK](./solana-staking/buildervault-wallet/nodejs/README.md)
48

5-
```mermaid
6-
sequenceDiagram
7-
autonumber
8-
participant StakeClient as Sample stake<br> client application
9-
participant StakeAPI as Stake Intent API
10-
participant Blockchain as Blockchain RPC API
11-
box Builder Vault
12-
participant TSM1 as MPC Wallet <br>(private key share 1)
13-
participant TSM2 as MPC Wallet <br>(private key share 2)
14-
end
9+
## Ethereum Staking
10+
- [Stake deposit from Builder Vault wallet with Golang SDK](./ethereum-staking/buildervault-wallet/golang/README.md)
11+
- [Stake deposit from Builder Vault wallet with TypeScript SDK](./ethereum-staking/buildervault-wallet/nodejs/README.md)
1512

16-
StakeClient ->> StakeAPI: get StakeIntent unsigned tx data <br>(amount, withdrawal & recipient address)
17-
StakeClient ->> Blockchain: get blockchain inputs (gas, nonce) for new tx<br>(sender wallet)
18-
StakeClient ->> StakeClient: construct unsigned tx
19-
StakeClient ->> TSM1: request signature (unsigned tx)
20-
TSM1 -->> StakeClient: return partial signature
21-
StakeClient ->> TSM2: request signature (unsigned tx)
22-
TSM2 -->> StakeClient: return partial signature
23-
StakeClient ->> StakeClient: combine partial signatures
24-
StakeClient ->> Blockchain: broadcast signed tx<br>(signed tx, deposit contract)
25-
```
26-
27-
<!--
28-
29-
# Get Plans
30-
http -b GET https://svc.blockdaemon.com/boss/v1/plans \
31-
X-API-KEY:$STAKE_API_KEY
32-
33-
# Post Intent
34-
http -b POST https://svc.blockdaemon.com/boss/v1/ethereum/holesky/stake-intents \
35-
X-API-KEY:$STAKE_API_KEY \
36-
accept:application/json \
37-
content-type:application/json \
38-
stakes:='[{"amount":"32000000000","withdrawal_address":"0x00000000219ab540356cBB839Cbe05303d7705Fa","fee_recipient":"0x93247f2209abcacf57b75a51dafae777f9dd38bc"}]'
39-
40-
# Get Intents
41-
http -b GET https://svc.blockdaemon.com/boss/v1/stake-intents?protocols=ethereum&networks=holesky \
42-
X-API-KEY:$STAKE_API_KEY
43-
```
44-
45-
sequenceDiagram
46-
autonumber
47-
participant StakeClient as Sample stake<br> client application
48-
participant StakeAPI as Stake Intent API
49-
participant Blockchain as Blockchain
50-
participant TSM as Sender Wallet Vault<br>(private key)
51-
52-
StakeClient ->> StakeAPI: get StakeIntent unsigned tx data <br>(amount, withdrawal & recipient address)
53-
StakeClient ->> Blockchain: get blockchain inputs for new tx<br>(gas fee, chainID, sender wallet nonce)
54-
StakeClient ->> StakeClient: construct unsigned tx
55-
StakeClient ->> TSM: request signature of unsigned tx
56-
TSM ->> StakeClient: return tx signature
57-
StakeClient ->> Blockchain: broadcast signed tx<br>(signed tx, deposit contract)
58-
--!>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
2+
## Sample go staking app demo
3+
4+
5+
```mermaid
6+
sequenceDiagram
7+
autonumber
8+
participant StakeClient as Sample stake<br> client application
9+
participant StakeAPI as Stake Intent API
10+
participant Blockchain as Blockchain RPC API
11+
box Builder Vault
12+
participant TSM1 as MPC Wallet <br>(private key share 1)
13+
participant TSM2 as MPC Wallet <br>(private key share 2)
14+
end
15+
16+
StakeClient ->> StakeAPI: get StakeIntent unsigned tx data <br>(amount, withdrawal & recipient address)
17+
StakeClient ->> Blockchain: get blockchain inputs (gas, nonce) for new tx<br>(sender wallet)
18+
StakeClient ->> StakeClient: construct unsigned tx
19+
StakeClient ->> TSM1: request signature (unsigned tx)
20+
TSM1 -->> StakeClient: return partial signature
21+
StakeClient ->> TSM2: request signature (unsigned tx)
22+
TSM2 -->> StakeClient: return partial signature
23+
StakeClient ->> StakeClient: combine partial signatures
24+
StakeClient ->> Blockchain: broadcast signed tx<br>(signed tx, deposit contract)
25+
```
26+
27+
<!--
28+
29+
# Get Plans
30+
http -b GET https://svc.blockdaemon.com/boss/v1/plans \
31+
X-API-KEY:$STAKE_API_KEY
32+
33+
# Post Intent
34+
http -b POST https://svc.blockdaemon.com/boss/v1/ethereum/holesky/stake-intents \
35+
X-API-KEY:$STAKE_API_KEY \
36+
accept:application/json \
37+
content-type:application/json \
38+
stakes:='[{"amount":"32000000000","withdrawal_address":"0x00000000219ab540356cBB839Cbe05303d7705Fa","fee_recipient":"0x93247f2209abcacf57b75a51dafae777f9dd38bc"}]'
39+
40+
# Get Intents
41+
http -b GET https://svc.blockdaemon.com/boss/v1/stake-intents?protocols=ethereum&networks=holesky \
42+
X-API-KEY:$STAKE_API_KEY
43+
```
44+
45+
sequenceDiagram
46+
autonumber
47+
participant StakeClient as Sample stake<br> client application
48+
participant StakeAPI as Stake Intent API
49+
participant Blockchain as Blockchain
50+
participant TSM as Sender Wallet Vault<br>(private key)
51+
52+
StakeClient ->> StakeAPI: get StakeIntent unsigned tx data <br>(amount, withdrawal & recipient address)
53+
StakeClient ->> Blockchain: get blockchain inputs for new tx<br>(gas fee, chainID, sender wallet nonce)
54+
StakeClient ->> StakeClient: construct unsigned tx
55+
StakeClient ->> TSM: request signature of unsigned tx
56+
TSM ->> StakeClient: return tx signature
57+
StakeClient ->> Blockchain: broadcast signed tx<br>(signed tx, deposit contract)
58+
--!>
File renamed without changes.
File renamed without changes.

main.go renamed to ethereum-staking/buildervault-wallet/golang/main.go

+10-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
"github.com/ethereum/go-ethereum/common"
1717
"github.com/ethereum/go-ethereum/core/types"
1818
"github.com/ethereum/go-ethereum/ethclient"
19-
"github.com/ethereum/go-ethereum/rlp"
2019
"github.com/ethereum/go-ethereum/rpc"
2120
"gitlab.com/Blockdaemon/go-tsm-sdkv2/tsm" // Builder Vault MPC SDK for wallet management
2221
"golang.org/x/sync/errgroup"
@@ -129,7 +128,13 @@ func craftTx(client *ethclient.Client, ethereumSenderAddress string, contractAdd
129128
Data: common.FromHex(txData),
130129
})
131130

132-
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())
131+
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())
132+
133+
raw, err := unsignedTx.MarshalBinary()
134+
if err != nil {
135+
panic(err)
136+
}
137+
fmt.Printf("\nCrafted unsigned transaction (hex encoded): 0x%x", raw)
133138

134139
// create a NewLondonSigner for EIP 1559 transactions
135140
signer := types.NewLondonSigner(chainID)
@@ -193,7 +198,7 @@ func signTx(unsignedTxHash []byte) []byte {
193198
copy(sigBytes[0:32], signature.R())
194199
copy(sigBytes[32:64], signature.S())
195200
sigBytes[64] = byte(signature.RecoveryID())
196-
fmt.Println("\nTransaction signature:\n", hex.EncodeToString(sigBytes))
201+
fmt.Println("\n\nTransaction signature (hex encoded):\n", hex.EncodeToString(sigBytes))
197202

198203
return sigBytes
199204
}
@@ -206,11 +211,11 @@ func sendTx(client *ethclient.Client, chainID *big.Int, unsignedTx *types.Transa
206211
panic(err)
207212
}
208213

209-
raw, err := rlp.EncodeToBytes(signedTx)
214+
raw, err := signedTx.MarshalBinary()
210215
if err != nil {
211216
panic(err)
212217
}
213-
fmt.Printf("\nSigned raw transaction: 0x%x", raw)
218+
fmt.Printf("\nSigned raw transaction (RLP encoded): 0x%x", raw)
214219

215220
err = client.SendTransaction(context.Background(), signedTx)
216221
if err != nil {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Blockdaemon Stake
2+
BLOCKDAEMON_API_KEY=
3+
BLOCKDAEMON_STAKE_API_KEY=
4+
ETHEREUM_NETWORK=holesky # mainnet | holesky
5+
ETHEREUM_WITHDRAWAL_ADDRESS=
6+
PLAN_ID= # Optional. If provided, will use a specific validator plan.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
2+
# Ethereum staking on Testnet with Builder Vault wallet
3+
4+
```mermaid
5+
sequenceDiagram
6+
autonumber
7+
participant StakeClient as Sample stake<br> client application
8+
participant StakeAPI as Stake Intent API
9+
participant Blockchain as Blockchain RPC API
10+
box Builder Vault
11+
participant TSM1 as MPC Wallet <br>(private key share 1)
12+
participant TSM2 as MPC Wallet <br>(private key share 2)
13+
end
14+
15+
StakeClient ->> StakeAPI: get StakeIntent unsigned tx data <br>(amount, withdrawal & recipient address)
16+
StakeClient ->> Blockchain: get blockchain inputs (gas, nonce) for new tx<br>(sender wallet)
17+
StakeClient ->> StakeClient: construct unsigned tx
18+
StakeClient ->> TSM1: request signature (unsigned tx)
19+
TSM1 -->> StakeClient: return partial signature
20+
StakeClient ->> TSM2: request signature (unsigned tx)
21+
TSM2 -->> StakeClient: return partial signature
22+
StakeClient ->> StakeClient: combine partial signatures
23+
StakeClient ->> Blockchain: broadcast signed tx<br>(signed tx, deposit contract)
24+
```
25+
26+
### Prerequisites
27+
- [Node.js](https://nodejs.org/en/download/package-manager) or use [code-spaces](https://codespaces.new/Blockdaemon/demo-buildervault-stakingAPI?quickstart=1)
28+
- Register for a demo Builder Vault tenant: https://www.blockdaemon.com/get-started/builder-vault-sandbox-registration
29+
- Download SDK bundle provided in registration email (extract authentication certificates)
30+
- Place Builder Vault authentication certificate key-pair `client.crt` & `client.key` in this nodejs folder
31+
- 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
32+
- 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
33+
- Speak to your CSM about getting credentials to the Blockdaemon nexus.sepior.net repo for the nodejs SDK.
34+
35+
### Step 1. Set environment variables in .env
36+
```shell
37+
cd ethereum-staking/buildervault-wallet/nodejs/
38+
cp .env.example .env
39+
```
40+
- update .env with API keys
41+
42+
### Step 2. Install package dependancies
43+
- replace NEXUS_USERNAME & NEXUS_PASSWORD with credential provided by your CSM
44+
```shell
45+
npm config set @sepior:registry=https://nexus.sepior.net/repository/sepior-nodejs-tsm-sdk-group/
46+
npm config set //nexus.sepior.net/repository/sepior-nodejs-tsm-sdk-group/:username=NEXUS_USERNAME
47+
npm config set //nexus.sepior.net/repository/sepior-nodejs-tsm-sdk-group/:\_password=`echo -n 'NEXUS_PASSWORD' | base64`
48+
npm install
49+
```
50+
51+
### Step 3. Launch ethereum-stake-bv.ts to auto-create the Builder Vault wallet address on first run
52+
```shell
53+
npm run start ethereum-stake-bv.ts
54+
```
55+
- note, on first run this step will fail as the wallet address has no funds
56+
- copy the new Ethereum wallet address and fund the account
57+
58+
### Step 4. Fund the new Ethereum wallet address with 33 ETH using faucets below
59+
- https://holesky-faucet.pk910.de/#/
60+
61+
### Step 5. Launch ethereum-stake-bv.ts to generate the Stake Intent request, sign the request with BuilderVault and broadcast the transaction
62+
```shell
63+
npm run start ethereum-stake-bv.ts
64+
```
65+
- (optional) decode the raw unsigned transaction to inspect the Blockdaemon provided attributes (https://rawtxdecode.in)
66+
- observe the confirmed transaction through the generated blockexplorer link

0 commit comments

Comments
 (0)