diff --git a/1-create-wallet/main.go b/1-create-wallet/main.go
index 61addfa..04d073b 100644
--- a/1-create-wallet/main.go
+++ b/1-create-wallet/main.go
@@ -4,6 +4,7 @@ import (
"context"
"crypto/x509"
"encoding/base64"
+ "encoding/hex"
"encoding/pem"
"fmt"
@@ -110,8 +111,14 @@ func main() {
}
}
+ publicKeyBytes, err := tsmutils.PKIXPublicKeyToCompressedPoint(pkixPublicKeys[0])
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println("Ethereum chain path m/44/60/0/0 derived wallet compressed public key:", hex.EncodeToString(publicKeyBytes))
+
// Convert the public key into an Ethereum address
- publicKeyBytes, err := tsmutils.PKIXPublicKeyToUncompressedPoint(pkixPublicKeys[0])
+ publicKeyBytes, err = tsmutils.PKIXPublicKeyToUncompressedPoint(pkixPublicKeys[0])
if err != nil {
panic(err)
}
diff --git a/2-craft-transaction/main.go b/2-craft-transaction/main.go
index 9b73540..7bf2068 100644
--- a/2-craft-transaction/main.go
+++ b/2-craft-transaction/main.go
@@ -15,11 +15,10 @@ import (
var protocol = "ethereum"
var network = "sepolia"
-var chainID = big.NewInt(11155111)
-var url = "https://svc.blockdaemon.com/universal/v1/" + protocol + "/" + network + "/"
+var url = "https://svc.blockdaemon.com"
var address = "..." // ! Set the wallet address created in step 1
-var destinationAddress = "0x6Cc9397c3B38739daCbfaA68EaD5F5D77Ba5F455" // Optional - override destination address which defaults back to faucet
-var amount = "0.003" // Set the amount to send in ETH
+var destinationAddress = "0x52b09e2c73849B25F9b0328e2d4b444e9bd1EF30" // Optional - override destination address which defaults back to faucet
+var amount = "0.001" // Set the amount to send in ETH
func main() {
@@ -30,7 +29,7 @@ func main() {
}
// * Get account balance https://docs.blockdaemon.com/reference/getlistofbalancesbyaddress
- res, err := http.Get(url + "account/" + address + "?apiKey=" + apiKey)
+ res, err := http.Get(url + "/universal/v1/" + protocol + "/" + network + "/account/" + address + "?apiKey=" + apiKey)
if err != nil {
panic(err)
}
@@ -54,21 +53,15 @@ func main() {
balanceFloat.Mul(balanceFloat, big.NewFloat(math.Pow10(-account[0].Currency.Decimals)))
fmt.Printf("Balance at account %s: %s %s\n", address, balanceFloat.Text('f', 18), account[0].Currency.Symbol)
- // * Transaction request - MaxFeePerGas and MaxPriorityFeePerGas are estimated automatically https://docs.blockdaemon.com/reference/txcreate
+ // * Transaction request - MaxFeePerGas and MaxPriorityFeePerGas are estimated automatically https://docs.blockdaemon.com/reference/txcreate-txapi
txRequest := struct {
- From string `json:"from"`
- To []struct {
- Destination string `json:"destination"`
- Amount string `json:"amount"`
- } `json:"to"`
+ From string `json:"from"`
+ To string `json:"to"`
+ Value string `json:"value"`
}{
- From: address,
- To: []struct {
- Destination string `json:"destination"`
- Amount string `json:"amount"`
- }{
- {Destination: destinationAddress, Amount: amount},
- },
+ From: address,
+ To: destinationAddress,
+ Value: amount,
}
reqBody, err := json.Marshal(txRequest)
@@ -77,7 +70,7 @@ func main() {
}
// Post unsigned transaction request
- res, err = http.Post(url+"tx/create?apiKey="+apiKey, "application/json", bytes.NewReader(reqBody))
+ res, err = http.Post(url+"/tx/v1/"+protocol+"-"+network+"/create?apiKey="+apiKey, "application/json", bytes.NewReader(reqBody))
if err != nil {
panic(err)
}
@@ -104,8 +97,8 @@ func main() {
panic(err)
}
- // * create a NewLondonSigner for EIP 1559 transactions
- signer := types.NewCancunSigner(chainID)
- fmt.Printf("Raw unsigned NewCancunSigner tx hash: %s\n", hex.EncodeToString(signer.Hash(unsignedTx).Bytes()))
+ // * create a NewLondonSigner EIP1559 transaction type hash
+ signer := types.NewLondonSigner(unsignedTx.ChainId())
+ fmt.Printf("Raw unsigned NewLondonSigner tx hash: %s\n", hex.EncodeToString(signer.Hash(unsignedTx).Bytes()))
}
diff --git a/3-sign-transaction/main.go b/3-sign-transaction/main.go
index 4eb1842..5a13280 100644
--- a/3-sign-transaction/main.go
+++ b/3-sign-transaction/main.go
@@ -3,10 +3,12 @@ package main
import (
"context"
"crypto/x509"
+ "encoding/asn1"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"fmt"
+ "math/big"
"sync"
"golang.org/x/sync/errgroup"
@@ -25,7 +27,7 @@ func main() {
// ! Specify the Builder Vailt TSM master key, created in step 1, to sign via the derived wallet chain path m/44/60/0/0
masterKeyID := "..."
- // ! Specify the NewCancunSigner unsigned transaction hash created in step 2
+ // ! Specify the NewLondonSigner unsigned transaction hash created in step 2
unsignedTxHash := "..."
// Decode server public keys to bytes for use in TLS client authentication
@@ -113,5 +115,22 @@ func main() {
copy(sigBytes[0:32], signature.R())
copy(sigBytes[32:64], signature.S())
sigBytes[64] = byte(signature.RecoveryID())
- fmt.Println("tx hash signature:", hex.EncodeToString(sigBytes))
+ fmt.Println("Tx hash signature:", hex.EncodeToString(sigBytes))
+
+ // Construct signature in ASN.1 DER format
+ derSignature := struct {
+ R, S *big.Int
+ V int
+ }{
+ R: new(big.Int).SetBytes(signature.R()),
+ S: new(big.Int).SetBytes(signature.S()),
+ V: signature.RecoveryID(),
+ }
+
+ // Marshal the structure to ASN.1 DER
+ der, err := asn1.Marshal(derSignature)
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println("Tx hash signature (DER encoded):", hex.EncodeToString(der))
}
diff --git a/4-broadcast-signed-transaction/main.go b/4-broadcast-signed-transaction/main.go
index 8417304..e938165 100644
--- a/4-broadcast-signed-transaction/main.go
+++ b/4-broadcast-signed-transaction/main.go
@@ -6,7 +6,6 @@ import (
"encoding/json"
"fmt"
"io"
- "math/big"
"net/http"
"os"
@@ -15,16 +14,18 @@ import (
var protocol = "ethereum"
var network = "sepolia"
-var chainID = big.NewInt(11155111)
-var url = "https://svc.blockdaemon.com/universal/v1/" + protocol + "/" + network + "/"
+var url = "https://svc.blockdaemon.com"
func main() {
- // ! Set the transactionHashSignature created in step 4
- transactionHashSignature := "..."
+ // ! Set the wallet public keu created in step 1
+ walletPublicKey := "..."
- // ! Set the rawUnsignedTransaction created in step 3
+ // ! Set the rawUnsignedTransaction created in step 2
rawUnsignedTransaction := "..."
+ // ! Set the DER transactionHashSignature created in step 3
+ transactionHashSignature := "..."
+
// Access token is required
apiKey := os.Getenv("ACCESS_TOKEN")
if apiKey == "" {
@@ -39,41 +40,41 @@ func main() {
panic(err)
}
- // Deserialize the transactionHashSignature
- transactionHashSignatureBytes, _ := hex.DecodeString(transactionHashSignature)
+ // * Compile the unsigned tx with the signatureand broadcast to the blockchain https://docs.blockdaemon.com/reference/txcompileandsend-txapi
+ txRequest := struct {
+ Unsigned_tx string `json:"unsigned_tx"`
+ Signature string `json:"signature"`
+ Public_key string `json:"public_key"`
+ }{
+ Unsigned_tx: rawUnsignedTransaction,
+ Signature: transactionHashSignature,
+ Public_key: walletPublicKey,
+ }
- // * Combine the unsigned transaction and the signature to create a signed transaction
- signedTx, err := unsignedTx.WithSignature(types.NewLondonSigner(chainID), transactionHashSignatureBytes)
+ reqBody, err := json.Marshal(txRequest)
if err != nil {
panic(err)
}
- // * Serialize the signed transaction for broadcast
- raw, err := signedTx.MarshalBinary()
+ res, err := http.Post(url+"/tx/v1/"+protocol+"-"+network+"/compile_and_send?apiKey="+apiKey, "application/json", bytes.NewReader(reqBody))
+ resbodyBytes, _ := io.ReadAll(res.Body)
if err != nil {
panic(err)
}
- fmt.Printf("\nRaw signed tx (RLP encoded): %x\n", raw)
+ defer res.Body.Close()
- // * Broadcast the signed transaction to the blockchain https://docs.blockdaemon.com/reference/txsend
- sendRequest := struct {
- TX string `json:"tx"`
- }{
- TX: hex.EncodeToString(raw),
+ // Check HTTP status code
+ if res.StatusCode != http.StatusOK {
+ panic(fmt.Errorf("invalid status code:%d %s %s", res.StatusCode, http.StatusText(res.StatusCode), resbodyBytes))
}
- reqBody, err := json.Marshal(sendRequest)
- if err != nil {
- panic(err)
+ // Parse broadcasted transaction response
+ var response struct {
+ ID string `json:"id"`
}
-
- res, _ := http.Post(url+"tx/send?apiKey="+apiKey, "application/json", bytes.NewReader(reqBody))
- resbodyBytes, _ := io.ReadAll(res.Body)
- defer res.Body.Close()
- if res.StatusCode != 200 {
- panic(fmt.Errorf("HTTP request %d error: %sn/ %s", res.StatusCode, http.StatusText(res.StatusCode), resbodyBytes))
- } else {
- fmt.Printf("\nBroadcasted transaction hash: https://"+network+".etherscan.io/tx/%s\n", signedTx.Hash().String())
+ if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
+ panic(err)
}
+ fmt.Printf("Broadcasted transaction hash: https://"+network+".etherscan.io/tx/%s\n", response.ID)
}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..4118a3a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,8 @@
+Evaluation License Notice:
+
+* Copyright © 2024 Blockdaemon Inc. - All Rights Reserved
+* Unauthorized copying of this file, via any medium is strictly prohibited
+* Proprietary and confidential, use only granted under applicable License
+
+Permission is hereby granted only under a license to use for testing and evaluating purposes only, to any authorized person obtaining access to this software, associated documentation files, and services (the "Product") to use the Product to support personal or professional operations subject to the License's restrictions. No user may rent, loan, sub-license, license, distribute, copy, decompile, disassemble, merge, modify, adapt, translate, reverse-engineer, make alterations to or derivative works based upon or derive source code from, nor attempt to grant any other rights to or in any whole or part of the Product any whole or part of the Product.
+This above copyright notice and this permission notice shall be included in all copies or substantial portions of the software.
\ No newline at end of file
diff --git a/README.md b/README.md
index ff599fd..f1f9024 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ sequenceDiagram
client ->> TSM3: request signature (unsigned tx hash)
TSM3 -->> client: return partial signature
client ->> client: combine partial signatures
- client ->> Blockchain: broadcast signed tx
(signed tx)
+ client ->> Blockchain: compile unsigned tx with signature and broadcast
(unsigned tx, signature, public key)
```
### Prerequisites
@@ -42,6 +42,7 @@ sequenceDiagram
```shell
go run 1-create-wallet/main.go
```
+ - note the new Ethereum Wallet public key (to be used for future signing)
- note the new Ethereum Wallet address and fund it with Sepolia ETH https://sepolia-faucet.pk910.de
- note the Builder Vault Master Key ID (to be used for future signing)
@@ -68,8 +69,9 @@ go run 3-sign-transaction/main.go
### Step 4. Broadcast signed raw transaction
- - set the transaction signature hash (created in step 3)
+ - set the wallet compressed public key (created in step 1)
- set the raw unsigned transaction (created in step 2)
+ - set the transaction signature hash (created in step 3)
```shell
go run 4-broadcast-signed-transaction/main.go
```
diff --git a/go.mod b/go.mod
index 4741596..8e4debb 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,8 @@
module buildervault-wallettransact
-go 1.21.0
+go 1.22
+
+toolchain go1.22.5
require (
github.com/ethereum/go-ethereum v1.14.0
@@ -11,7 +13,7 @@ require (
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect
- github.com/btcsuite/btcd v0.22.0-beta // indirect
+ github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.3 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.12.1 // indirect
diff --git a/go.sum b/go.sum
index bc87a8d..36704ba 100644
--- a/go.sum
+++ b/go.sum
@@ -12,6 +12,8 @@ github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo=
github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
+github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7Nzl7WHaOTlTr5hIrR4n1NM4v9n4Kw=
+github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0=