Skip to content

Commit

Permalink
Merge pull request #33 from coinbase/solana-staking-example
Browse files Browse the repository at this point in the history
This PR adds an abstraction around the SDK's signing support in order to support signing staking operations for ETH and SOL using the same interface. This PR relies heavily on Golang's stdlib `crypto/rand`.

Alongside these changes, I included numerous Solana stake examples to provide full examples of how to stake SOL.

Our current Solana staking endpoint can take significant periods of time to get data from the underlying Solana nodes, so I've added the ability to modify the timeout on the client.
  • Loading branch information
ProfMoo authored Sep 20, 2024
2 parents 752e2ff + f4e1b64 commit b41b1dc
Show file tree
Hide file tree
Showing 22 changed files with 1,253 additions and 518 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mocks:
mockery --disable-version-string --name AssetsAPI --keeptree --dir gen/client --output pkg/mocks
mockery --disable-version-string --name StakeAPI --keeptree --dir gen/client --output pkg/mocks
mockery --disable-version-string --name ValidatorsAPI --keeptree --dir gen/client --output pkg/mocks
mockery --disable-version-string --name Signable --keeptree --dir pkg/coinbase --output pkg/mocks

.PHONY: docs
docs:
Expand Down
94 changes: 0 additions & 94 deletions examples/ethereum/build-staking-operation/main.go

This file was deleted.

49 changes: 49 additions & 0 deletions examples/ethereum/list-staking-balances/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

import (
"context"
"log"
"os"
"time"

"github.com/coinbase/coinbase-sdk-go/pkg/coinbase"
)

/*
* This example code list historical staking balances for any ETH validator or Shared ETH Staking wallet address.
* For example: the addresses:
* * Shared ETH Staking wallet address: 0xddb00798137e9e7cc89f1e9679e6ce6ea580b8f9
* * ETH Validator: 0xadbf3776d60b3f9dd30cb3257b50583898745deb40cb6cb842120753bf055f6c3863e0f5bdb5c403d9aa5a275ce165e8
* Run the code with 'go run examples/ethereum/list-staking-balances/main.go <api_key_file_path> <wallet_address>'
*/

func main() {
ctx := context.Background()

client, err := coinbase.NewClient(
coinbase.WithAPIKeyFromJSON(os.Args[1]),
)
if err != nil {
log.Fatalf("error creating coinbase client: %v", err)
}

address := coinbase.NewExternalAddress(
"ethereum-mainnet",
os.Args[1],
)

balances, err := client.ListHistoricalStakingBalances(
ctx,
coinbase.Eth,
address,
time.Now().Add(-7*24*time.Hour),
time.Now(),
)
if err != nil {
log.Fatalf("error listing balances: %v", err)
}

for _, balance := range balances {
println(balance)
}
}
51 changes: 51 additions & 0 deletions examples/ethereum/list-staking-rewards/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
"context"
"log"
"os"
"time"

api "github.com/coinbase/coinbase-sdk-go/gen/client"
"github.com/coinbase/coinbase-sdk-go/pkg/coinbase"
)

/*
* This example code list historical staking rewards for any ETH validator or Shared ETH Staking wallet address.
* For example: the addresses:
* * Shared ETH Staking wallet address: 0xddb00798137e9e7cc89f1e9679e6ce6ea580b8f9
* * ETH Validator: 0xa1d1ad0714035353258038e964ae9675dc0252ee22cea896825c01458e1807bfad2f9969338798548d9858a571f7425c
* Run the code with 'go run examples/ethereum/list-staking-rewards/main.go <api_key_file_path> <wallet_address>'
*/

func main() {
ctx := context.Background()

client, err := coinbase.NewClient(
coinbase.WithAPIKeyFromJSON(os.Args[1]),
)
if err != nil {
log.Fatalf("error creating coinbase client: %v", err)
}

address := coinbase.NewExternalAddress(
"ethereum-mainnet",
os.Args[1],
)

rewards, err := client.ListStakingRewards(
ctx,
coinbase.Eth,
[]coinbase.Address{*address},
time.Now().Add(-7*24*time.Hour),
time.Now(),
api.STAKINGREWARDFORMAT_USD,
)
if err != nil {
log.Fatalf("error listing rewards: %v", err)
}

for _, reward := range rewards {
println(reward.ToJSON())
}
}
83 changes: 83 additions & 0 deletions examples/ethereum/stake/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package main

import (
"context"
"log"
"math/big"
"os"

"github.com/coinbase/coinbase-sdk-go/pkg/coinbase"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)

/*
* This example code stakes ETH on the Holesky network via Shared ETH Staking.
* Run the code with 'go run examples/ethereum/stake/main.go <api_key_file_path> <wallet_address> <wallet_private_key>'
*/

func main() {
ctx := context.Background()

client, err := coinbase.NewClient(
coinbase.WithAPIKeyFromJSON(os.Args[1]),
)
if err != nil {
log.Fatalf("error creating coinbase client: %v", err)
}

address := coinbase.NewExternalAddress("ethereum-holesky", os.Args[2])

stakeableBalance, err := client.GetStakeableBalance(ctx, coinbase.Eth, address, coinbase.WithStakingBalanceMode(coinbase.StakingOperationModePartial))
if err != nil {
log.Fatalf("error getting stakeable balance: %v", err)
}

log.Printf("stakeable balance: %s\n", stakeableBalance)

stakeOperation, err := client.BuildStakeOperation(
ctx,
big.NewFloat(0.0001),
coinbase.Eth,
address,
coinbase.WithStakingOperationMode(coinbase.StakingOperationModePartial),
)
if err != nil {
log.Fatalf("error building staking operation: %v", err)
}

log.Printf("staking operation ID: %s\n", stakeOperation.ID())

key, err := crypto.HexToECDSA(os.Args[3])
if err != nil {
log.Fatal(err)
}

// Sign the transactions within staking operation resource with your private key.
err = stakeOperation.Sign(key)
if err != nil {
log.Fatal(err)
}

// For Holesky, publicly available RPC URL's can be found here https://chainlist.org/chain/17000
ethClient, err := ethclient.Dial("<RPC_NODE_URL>")
if err != nil {
log.Fatal(err)
}

// Broadcast each of the signed transactions to the network.
for _, transaction := range stakeOperation.Transactions() {
rawTx := transaction.Raw()
ethTx, ok := rawTx.(*types.Transaction)
if !ok {
log.Fatal("failed to cast transaction to Ethereum transaction")
}

if err := ethClient.SendTransaction(context.Background(), ethTx); err != nil {
log.Fatal(err)
}

log.Printf("Broadcasted transaction hash: %s", ethTx.Hash().Hex())
}
}
Loading

0 comments on commit b41b1dc

Please sign in to comment.