diff --git a/.github/workflows/test.yaml b/.github/workflows/ci.yaml
similarity index 76%
rename from .github/workflows/test.yaml
rename to .github/workflows/ci.yaml
index 4c06f2f..f14e9ca 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/ci.yaml
@@ -1,4 +1,4 @@
-name: Go Test
+name: CI
on:
push:
@@ -12,7 +12,8 @@ permissions:
contents: read
jobs:
- test:
+ ci:
+ name: ci
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -25,5 +26,11 @@ jobs:
- name: Build
run: go build -v ./...
+ - name: Lint
+ uses: golangci/golangci-lint-action@v4
+ with:
+ version: v1.54.2
+ args: --timeout=3m
+
- name: Test
uses: robherley/go-test-action@v0
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
deleted file mode 100644
index 610eff2..0000000
--- a/.github/workflows/lint.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-name: Go Lint
-
-on:
- push:
- branches:
- - master
- # We only run tests when we open PRs (and not for ex: on every commit)
- # to avoid running workflows too frequently and incurring costs
- pull_request:
-
-permissions:
- contents: read
-
-jobs:
- lint:
- name: lint
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-go@v5
- with:
- go-version: "1.20"
- cache: false
- - name: golangci-lint
- uses: golangci/golangci-lint-action@v4
- with:
- version: v1.54.2
- args: --timeout=3m
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
new file mode 100644
index 0000000..f9c60fd
--- /dev/null
+++ b/.github/workflows/release.yaml
@@ -0,0 +1,47 @@
+name: Version 🔖
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - '.version'
+
+concurrency: ${{ github.workflow }}-${{ github.ref }}
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ environment: release
+ permissions:
+ contents: write
+ id-token: write
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Get version from .version file
+ id: release_version
+ run: echo "VERSION=$(cat .version")" >> $GITHUB_OUTPUT
+
+ - name: Check if tag exists
+ id: check_tag
+ run: |
+ git fetch --tags
+ if git rev-parse "v${{ steps.release_version.outputs.VERSION }}" >/dev/null 2>&1; then
+ echo "::set-output name=EXISTS::true"
+ fi
+
+ - name: Create Release
+ if: steps.check_tag.outputs.EXISTS != 'true'
+ uses: softprops/action-gh-release@v2
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: v${{ steps.release_version.outputs.VERSION }}
+ name: Release v${{ steps.release_version.outputs.VERSION }}
+ draft: false
+ prerelease: false
+ generate_release_notes: true
+ make_latest: true
diff --git a/.version b/.version
new file mode 100644
index 0000000..8f0916f
--- /dev/null
+++ b/.version
@@ -0,0 +1 @@
+0.5.0
diff --git a/README.md b/README.md
index 87323ff..3538a20 100644
--- a/README.md
+++ b/README.md
@@ -64,7 +64,6 @@ func main() {
Parameters: &api.EthereumKilnStakingParameters_StakeParameters{
StakeParameters: &api.EthereumKilnStakeParameters{
StakerAddress: "0xdb816889F2a7362EF242E5a717dfD5B38Ae849FE",
- IntegratorContractAddress: "0xA55416de5DE61A0AC1aa8970a280E04388B1dE4b",
Amount: &api.Amount{
Value: "20",
Currency: "ETH",
@@ -91,7 +90,82 @@ func main() {
Output
```text
- 2024/03/28 11:43:49 Workflow created: projects/62376b2f-3f24-42c9-9025-d576a3c06d6f/workflows/ffbf9b45-c57b-49cb-a4d5-fdab66d8cb25
+ 2024/03/28 11:43:49 Workflow created: workflows/ffbf9b45-c57b-49cb-a4d5-fdab66d8cb25
+ ```
+
+
+
+### Stake SOL :diamond_shape_with_a_dot_inside:
+
+This code sample creates an SOL staking workflow. View the full code sample [here](examples/solana/create-workflow/main.go)
+
+
+ Code Sample
+
+```golang
+// examples/solana/create-workflow/main.go
+package main
+
+import (
+ "context"
+ "log"
+
+ "github.com/coinbase/staking-client-library-go/auth"
+ "github.com/coinbase/staking-client-library-go/client"
+ "github.com/coinbase/staking-client-library-go/client/options"
+ api "github.com/coinbase/staking-client-library-go/gen/go/coinbase/staking/orchestration/v1"
+)
+
+func main() {
+ ctx := context.Background()
+
+ // Loads the API key from the default location.
+ apiKey, err := auth.NewAPIKey(auth.WithLoadAPIKeyFromFile(true))
+ if err != nil {
+ log.Fatalf("error loading API key: %s", err.Error())
+ }
+
+ // Creates the Coinbase Staking API client.
+ stakingClient, err := client.New(ctx, options.WithAPIKey(apiKey))
+ if err != nil {
+ log.Fatalf("error instantiating staking client: %s", err.Error())
+ }
+
+ req := &api.CreateWorkflowRequest{
+ Workflow: &api.Workflow{
+ Action: "protocols/solana/networks/devnet/actions/stake",
+ StakingParameters: &api.Workflow_SolanaStakingParameters{
+ SolanaStakingParameters: &api.SolanaStakingParameters{
+ Parameters: &api.SolanaStakingParameters_StakeParameters{
+ StakeParameters: &api.SolanaStakeParameters{
+ WalletAddress: walletAddress,
+ Amount: &api.Amount{
+ Value: amount,
+ Currency: currency,
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ workflow, err := stakingClient.Orchestration.CreateWorkflow(ctx, req)
+ if err != nil {
+ log.Fatalf("couldn't create workflow: %s", err.Error())
+ }
+
+ log.Printf("Workflow created: %s", workflow.Name)
+}
+```
+
+
+
+
+ Output
+
+ ```text
+ 2024/03/28 11:43:49 Workflow created: workflows/6bd9fd82-8b9d-4a49-9039-f95bb850a7a2
```
diff --git a/examples/ethereum/create-and-process-workflow/main.go b/examples/ethereum/create-and-process-workflow/main.go
index fbf0fff..b772285 100644
--- a/examples/ethereum/create-and-process-workflow/main.go
+++ b/examples/ethereum/create-and-process-workflow/main.go
@@ -25,10 +25,9 @@ const (
privateKey = ""
// TODO: Replace with your staker addresses and amount.
- stakerAddress = ""
- integratorContractAddress = "0xA55416de5DE61A0AC1aa8970a280E04388B1dE4b"
- amount = "11"
- currency = "ETH"
+ stakerAddress = ""
+ amount = "11"
+ currency = "ETH"
)
// An example function to demonstrate how to use the staking client libraries.
@@ -59,8 +58,7 @@ func main() {
EthereumKilnStakingParameters: &api.EthereumKilnStakingParameters{
Parameters: &api.EthereumKilnStakingParameters_StakeParameters{
StakeParameters: &api.EthereumKilnStakeParameters{
- StakerAddress: stakerAddress,
- IntegratorContractAddress: integratorContractAddress,
+ StakerAddress: stakerAddress,
Amount: &api.Amount{
Value: amount,
Currency: currency,
diff --git a/examples/ethereum/create-workflow/main.go b/examples/ethereum/create-workflow/main.go
index 216f365..eb48f6b 100644
--- a/examples/ethereum/create-workflow/main.go
+++ b/examples/ethereum/create-workflow/main.go
@@ -8,6 +8,8 @@ import (
"context"
"log"
+ "google.golang.org/protobuf/encoding/protojson"
+
"github.com/coinbase/staking-client-library-go/auth"
"github.com/coinbase/staking-client-library-go/client"
"github.com/coinbase/staking-client-library-go/client/options"
@@ -36,8 +38,7 @@ func main() {
EthereumKilnStakingParameters: &api.EthereumKilnStakingParameters{
Parameters: &api.EthereumKilnStakingParameters_StakeParameters{
StakeParameters: &api.EthereumKilnStakeParameters{
- StakerAddress: "0xdb816889F2a7362EF242E5a717dfD5B38Ae849FE",
- IntegratorContractAddress: "0xA55416de5DE61A0AC1aa8970a280E04388B1dE4b",
+ StakerAddress: "0xdb816889F2a7362EF242E5a717dfD5B38Ae849FE",
Amount: &api.Amount{
Value: "20",
Currency: "ETH",
@@ -54,5 +55,10 @@ func main() {
log.Fatalf("couldn't create workflow: %s", err.Error())
}
- log.Printf("Workflow created: %s", workflow.Name)
+ marshaled, err := protojson.MarshalOptions{Indent: " ", Multiline: true}.Marshal(workflow)
+ if err != nil {
+ log.Fatalf("error marshaling reward: %s", err.Error())
+ }
+
+ log.Printf("Workflow created: \n%s", marshaled)
}
diff --git a/examples/solana/create-and-process-workflow/main.go b/examples/solana/create-and-process-workflow/main.go
new file mode 100644
index 0000000..9ef5115
--- /dev/null
+++ b/examples/solana/create-and-process-workflow/main.go
@@ -0,0 +1,159 @@
+/*
+ * This example code, demonstrates staking client library usage for performing e2e staking on Solana.
+ */
+
+package main
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "time"
+
+ "github.com/coinbase/staking-client-library-go/auth"
+ "github.com/coinbase/staking-client-library-go/client"
+ stakingerrors "github.com/coinbase/staking-client-library-go/client/errors"
+ "github.com/coinbase/staking-client-library-go/client/options"
+ "github.com/coinbase/staking-client-library-go/client/orchestration"
+ api "github.com/coinbase/staking-client-library-go/gen/go/coinbase/staking/orchestration/v1"
+ "github.com/coinbase/staking-client-library-go/internal/signer"
+)
+
+const (
+ // TODO: Replace with your private key.
+ privateKey = ""
+
+ // TODO: Replace with your wallet addresses and amount.
+ walletAddress = ""
+ amount = "100000000"
+ currency = "SOL"
+)
+
+// An example function to demonstrate how to use the staking client libraries.
+func main() {
+ ctx := context.Background()
+
+ apiKey, err := auth.NewAPIKey(auth.WithLoadAPIKeyFromFile(true))
+ if err != nil {
+ log.Fatalf("error loading API key: %s", err.Error())
+ }
+
+ authOpt := options.WithAPIKey(apiKey)
+
+ // Create a staking client.
+ stakingClient, err := client.New(ctx, authOpt)
+ if err != nil {
+ log.Fatalf("error instantiating staking client: %s", err.Error())
+ }
+
+ if privateKey == "" || walletAddress == "" {
+ log.Fatalf("privateKey and walletAddress must be set")
+ }
+
+ req := &api.CreateWorkflowRequest{
+ Workflow: &api.Workflow{
+ Action: "protocols/solana/networks/devnet/actions/stake",
+ StakingParameters: &api.Workflow_SolanaStakingParameters{
+ SolanaStakingParameters: &api.SolanaStakingParameters{
+ Parameters: &api.SolanaStakingParameters_StakeParameters{
+ StakeParameters: &api.SolanaStakeParameters{
+ WalletAddress: walletAddress,
+ Amount: &api.Amount{
+ Value: amount,
+ Currency: currency,
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ workflow, err := stakingClient.Orchestration.CreateWorkflow(ctx, req)
+ if err != nil {
+ sae := stakingerrors.FromError(err)
+ _ = sae.Print()
+ os.Exit(1)
+ }
+
+ log.Printf("Workflow created %s ...\n", workflow.Name)
+
+ // Run loop until workflow reaches a terminal state
+ for {
+ // Get the latest workflow state
+ workflow, err = stakingClient.Orchestration.GetWorkflow(ctx, &api.GetWorkflowRequest{Name: workflow.Name})
+ if err != nil {
+ log.Fatalf(fmt.Errorf("error getting workflow: %w", err).Error())
+ }
+
+ printWorkflowProgressDetails(workflow)
+
+ // If workflow is in WAITING_FOR_EXT_BROADCAST state, sign, broadcast the transaction and update the workflow.
+ if orchestration.WorkflowWaitingForExternalBroadcast(workflow) {
+ unsignedTx := workflow.Steps[workflow.GetCurrentStepId()].GetTxStepOutput().GetUnsignedTx()
+
+ // Logic to sign the transaction. This can be substituted with any other signing mechanism.
+ log.Printf("Signing unsigned tx %s ...\n", unsignedTx)
+
+ signedTx, err := signer.New("solana").SignTransaction([]string{privateKey}, &signer.UnsignedTx{Data: []byte(unsignedTx)})
+ if err != nil {
+ log.Fatalf(fmt.Errorf("error signing transaction: %w", err).Error())
+ }
+
+ // Add logic to broadcast the tx here.
+ fmt.Printf("Please broadcast this signed tx %s externally and return back the tx hash via the PerformWorkflowStep API ...\n", signedTx)
+ break
+ } else if orchestration.WorkflowFinished(workflow) {
+ break
+ }
+
+ // Sleep for 1 second before polling for workflow status again
+ time.Sleep(1 * time.Second)
+ }
+}
+
+func printWorkflowProgressDetails(workflow *api.Workflow) {
+ if len(workflow.GetSteps()) <= 0 {
+ fmt.Println("Waiting for steps to be created ...")
+ time.Sleep(2 * time.Second)
+ }
+
+ step := workflow.Steps[workflow.GetCurrentStepId()]
+
+ createTime := workflow.GetCreateTime().AsTime()
+ updateTime := workflow.GetUpdateTime().AsTime()
+ runtime := updateTime.Sub(createTime)
+
+ var stepDetails string
+
+ switch step.GetOutput().(type) {
+ case *api.WorkflowStep_TxStepOutput:
+ stepDetails = fmt.Sprintf("state: %s tx hash: %s",
+ step.GetTxStepOutput().GetState().String(),
+ step.GetTxStepOutput().GetTxHash(),
+ )
+ case *api.WorkflowStep_WaitStepOutput:
+ stepDetails = fmt.Sprintf("state: %s current: %d target: %d",
+ step.GetWaitStepOutput().GetState().String(),
+ step.GetWaitStepOutput().GetCurrent(),
+ step.GetWaitStepOutput().GetTarget(),
+ )
+ }
+
+ if orchestration.WorkflowFinished(workflow) {
+ log.Printf("Workflow reached end state - step name: %s %s workflow state: %s runtime: %v\n",
+ step.GetName(),
+ stepDetails,
+ workflow.GetState().String(),
+ runtime,
+ )
+ } else {
+ log.Printf("Waiting for workflow to finish - step name: %s %s workflow state: %s runtime: %v\n",
+ step.GetName(),
+ stepDetails,
+ workflow.GetState().String(),
+ runtime,
+ )
+ }
+}
diff --git a/examples/solana/create-workflow/main.go b/examples/solana/create-workflow/main.go
index e1dd815..da39e6d 100644
--- a/examples/solana/create-workflow/main.go
+++ b/examples/solana/create-workflow/main.go
@@ -6,61 +6,49 @@ package main
import (
"context"
- "fmt"
"log"
- "os"
- "time"
+
+ "google.golang.org/protobuf/encoding/protojson"
"github.com/coinbase/staking-client-library-go/auth"
"github.com/coinbase/staking-client-library-go/client"
stakingerrors "github.com/coinbase/staking-client-library-go/client/errors"
"github.com/coinbase/staking-client-library-go/client/options"
- "github.com/coinbase/staking-client-library-go/client/orchestration"
api "github.com/coinbase/staking-client-library-go/gen/go/coinbase/staking/orchestration/v1"
- "github.com/coinbase/staking-client-library-go/internal/signer"
+ "os"
)
const (
- // TODO: Replace with your private key.
- privateKey = ""
-
// TODO: Replace with your wallet addresses and amount.
- walletAddress = ""
- validatorAddress = "GkqYQysEGmuL6V2AJoNnWZUz2ZBGWhzQXsJiXm2CLKAN"
- amount = "100000000"
- currency = "SOL"
+ walletAddress = "8rMGARtkJY5QygP1mgvBFLsE9JrvXByARJiyNfcSE5Z"
+ amount = "100000000"
+ currency = "SOL"
)
// An example function to demonstrate how to use the staking client libraries.
func main() {
ctx := context.Background()
+ // Loads the API key from the default location.
apiKey, err := auth.NewAPIKey(auth.WithLoadAPIKeyFromFile(true))
if err != nil {
log.Fatalf("error loading API key: %s", err.Error())
}
- authOpt := options.WithAPIKey(apiKey)
-
- // Create a staking client.
- stakingClient, err := client.New(ctx, authOpt)
+ // Creates the Coinbase Staking API client.
+ stakingClient, err := client.New(ctx, options.WithAPIKey(apiKey))
if err != nil {
log.Fatalf("error instantiating staking client: %s", err.Error())
}
- if privateKey == "" || walletAddress == "" {
- log.Fatalf("privateKey and walletAddress must be set")
- }
-
req := &api.CreateWorkflowRequest{
Workflow: &api.Workflow{
- Action: "protocols/solana/networks/testnet/actions/stake",
+ Action: "protocols/solana/networks/devnet/actions/stake",
StakingParameters: &api.Workflow_SolanaStakingParameters{
SolanaStakingParameters: &api.SolanaStakingParameters{
Parameters: &api.SolanaStakingParameters_StakeParameters{
StakeParameters: &api.SolanaStakeParameters{
- WalletAddress: walletAddress,
- ValidatorAddress: validatorAddress,
+ WalletAddress: walletAddress,
Amount: &api.Amount{
Value: amount,
Currency: currency,
@@ -79,83 +67,10 @@ func main() {
os.Exit(1)
}
- log.Printf("Workflow created %s ...\n", workflow.Name)
-
- // Run loop until workflow reaches a terminal state
- for {
- // Get the latest workflow state
- workflow, err = stakingClient.Orchestration.GetWorkflow(ctx, &api.GetWorkflowRequest{Name: workflow.Name})
- if err != nil {
- log.Fatalf(fmt.Errorf("error getting workflow: %w", err).Error())
- }
-
- printWorkflowProgressDetails(workflow)
-
- // If workflow is in WAITING_FOR_EXT_BROADCAST state, sign, broadcast the transaction and update the workflow.
- if orchestration.WorkflowWaitingForExternalBroadcast(workflow) {
- unsignedTx := workflow.Steps[workflow.GetCurrentStepId()].GetTxStepOutput().GetUnsignedTx()
-
- // Logic to sign the transaction. This can be substituted with any other signing mechanism.
- log.Printf("Signing unsigned tx %s ...\n", unsignedTx)
-
- signedTx, err := signer.New("solana").SignTransaction([]string{privateKey}, &signer.UnsignedTx{Data: []byte(unsignedTx)})
- if err != nil {
- log.Fatalf(fmt.Errorf("error signing transaction: %w", err).Error())
- }
-
- // Add logic to broadcast the tx here.
- fmt.Printf("Please broadcast this signed tx %s externally and return back the tx hash via the PerformWorkflowStep API ...\n", signedTx)
- break
- } else if orchestration.WorkflowFinished(workflow) {
- break
- }
-
- // Sleep for 1 second before polling for workflow status again
- time.Sleep(1 * time.Second)
- }
-}
-
-func printWorkflowProgressDetails(workflow *api.Workflow) {
- if len(workflow.GetSteps()) <= 0 {
- fmt.Println("Waiting for steps to be created ...")
- time.Sleep(2 * time.Second)
- }
-
- step := workflow.Steps[workflow.GetCurrentStepId()]
-
- createTime := workflow.GetCreateTime().AsTime()
- updateTime := workflow.GetUpdateTime().AsTime()
- runtime := updateTime.Sub(createTime)
-
- var stepDetails string
-
- switch step.GetOutput().(type) {
- case *api.WorkflowStep_TxStepOutput:
- stepDetails = fmt.Sprintf("state: %s tx hash: %s",
- step.GetTxStepOutput().GetState().String(),
- step.GetTxStepOutput().GetTxHash(),
- )
- case *api.WorkflowStep_WaitStepOutput:
- stepDetails = fmt.Sprintf("state: %s current: %d target: %d",
- step.GetWaitStepOutput().GetState().String(),
- step.GetWaitStepOutput().GetCurrent(),
- step.GetWaitStepOutput().GetTarget(),
- )
+ marshaled, err := protojson.MarshalOptions{Indent: " ", Multiline: true}.Marshal(workflow)
+ if err != nil {
+ log.Fatalf("error marshaling reward: %s", err.Error())
}
- if orchestration.WorkflowFinished(workflow) {
- log.Printf("Workflow reached end state - step name: %s %s workflow state: %s runtime: %v\n",
- step.GetName(),
- stepDetails,
- workflow.GetState().String(),
- runtime,
- )
- } else {
- log.Printf("Waiting for workflow to finish - step name: %s %s workflow state: %s runtime: %v\n",
- step.GetName(),
- stepDetails,
- workflow.GetState().String(),
- runtime,
- )
- }
+ log.Printf("Workflow created: \n%s", marshaled)
}