diff --git a/README.md b/README.md index 7a01f86..8fd4598 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,7 @@ import { StakingClient } from "@coinbase/staking-client-library-ts"; const client = new StakingClient(); -client.Ethereum.stake( - 'holesky', - true, - '0xdb816889F2a7362EF242E5a717dfD5B38Ae849FE', // replace with your wallet address - '0xA55416de5DE61A0AC1aa8970a280E04388B1dE4b', - '123', -) +client.Ethereum.stake('holesky', '0xdb816889F2a7362EF242E5a717dfD5B38Ae849FE', '123') .then((workflow) => { console.log('Workflow created %s', workflow.name); }) @@ -57,7 +51,40 @@ client.Ethereum.stake( Output ```text - Workflow created: projects/62376b2f-3f24-42c9-9025-d576a3c06d6f/workflows/ffbf9b45-c57b-49cb-a4d5-fdab66d8cb25 + Workflow created workflows/c34df125-a989-438d-8451-bd403423986a + ``` + + + +### Stake SOL :diamond_shape_with_a_dot_inside: + +This code sample creates a SOL staking workflow. View the full code sample [here](examples/solana/create-workflow.ts) + +
+ Code Sample + +```typescript +// examples/solana/create-workflow.ts +import { StakingClient } from "@coinbase/staking-client-library-ts"; + +const client = new StakingClient(); + +client.Solana.stake('devnet', '8rMGARtkJY5QygP1mgvBFLsE9JrvXByARJiyNfcSE5Z', '100000000') + .then((workflow) => { + console.log('Workflow created %s', workflow.name); + }) + .catch(() => { + throw new Error(`Error creating workflow`); + }); +``` + +
+ +
+ Output + + ```text + Workflow created workflows/e6373b20-edf0-4cf9-91ea-709328d0d63e ```
@@ -153,6 +180,28 @@ client.Ethereum.listRewards(filter).then((resp) => { +## Build + +Here are some helpful commands to build and lint the project: + +### Generate client code + +```shell +npm run gen +``` + +### Lint + +```shell +npm run lint +``` + +### Lint Fix + +```shell +npm run lint-fix +``` + ## Documentation There are numerous examples in the [`examples directory`](./examples) to help get you started. For even more, refer to our [documentation website](https://docs.cdp.coinbase.com/) for detailed definitions, API specifications, integration guides, and more! diff --git a/docs/openapi/orchestration.swagger.json b/docs/openapi/orchestration.swagger.json index d5ce955..3a0deb1 100644 --- a/docs/openapi/orchestration.swagger.json +++ b/docs/openapi/orchestration.swagger.json @@ -481,23 +481,7 @@ "in": "body", "required": true, "schema": { - "type": "object", - "properties": { - "step": { - "type": "integer", - "format": "int32", - "description": "The index of the step to be performed." - }, - "data": { - "type": "string", - "description": "Transaction metadata. This is either the signed transaction or transaction hash depending on the workflow's broadcast method." - } - }, - "description": "The request message for PerformWorkflowStep.", - "required": [ - "step", - "data" - ] + "$ref": "#/definitions/StakingServicePerformWorkflowStepBody" } } ], @@ -745,6 +729,25 @@ "default": "BALANCE_STATE_UNSPECIFIED", "description": "Represents the different states a stake account balance can have.\nUsed to check to see if stake is actively earning rewards or ready to be withdrawn.\n\n - BALANCE_STATE_UNSPECIFIED: The balance is not known.\n - BALANCE_STATE_INACTIVE: The balance is not actively staking.\n - BALANCE_STATE_ACTIVATING: The balance is in a warm up period and will activate in the next epoch.\n - BALANCE_STATE_ACTIVE: The balance is actively staking and earning rewards.\n - BALANCE_STATE_DEACTIVATING: The balance is in a cool down period and will be deactivated in the next epoch." }, + "StakingServicePerformWorkflowStepBody": { + "type": "object", + "properties": { + "step": { + "type": "integer", + "format": "int32", + "description": "The index of the step to be performed." + }, + "data": { + "type": "string", + "description": "Transaction metadata. This is either the signed transaction or transaction hash depending on the workflow's broadcast method." + } + }, + "description": "The request message for PerformWorkflowStep.", + "required": [ + "step", + "data" + ] + }, "WaitStepOutputWaitUnit": { "type": "string", "enum": [ @@ -809,6 +812,36 @@ }, "description": "The amount of a token you wish to perform an action\nwith." }, + "v1BulkTxStepOutput": { + "type": "object", + "properties": { + "unsignedTxs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The unsigned transactions that must be signed and broadcasted.", + "readOnly": true + }, + "state": { + "$ref": "#/definitions/v1BulkTxStepOutputState", + "description": "The state of the bulk tx step.", + "readOnly": true + } + }, + "description": "The details of multiple transactions being constructed and broadcasted to the network." + }, + "v1BulkTxStepOutputState": { + "type": "string", + "enum": [ + "STATE_UNSPECIFIED", + "STATE_IN_PROGRESS", + "STATE_FAILED", + "STATE_COMPLETED" + ], + "default": "STATE_UNSPECIFIED", + "description": "State defines an enumeration of states for a staking transaction.\n\n - STATE_UNSPECIFIED: Unspecified transaction state.\n - STATE_IN_PROGRESS: Txs construction in progress.\n - STATE_FAILED: Tx construction failed.\n - STATE_COMPLETED: Tx construction completed." + }, "v1Contract": { "type": "object", "properties": { @@ -1524,6 +1557,11 @@ "$ref": "#/definitions/v1ProvisionInfraStepOutput", "description": "The details for provisioned infrastructure.", "readOnly": true + }, + "bulkTxStepOutput": { + "$ref": "#/definitions/v1BulkTxStepOutput", + "description": "The bulk tx step output (e.g. transaction metadata such as unsigned tx, signed tx etc).", + "readOnly": true } }, "description": "The information for a step in the workflow.", diff --git a/docs/openapi/rewards.swagger.json b/docs/openapi/rewards.swagger.json index dfd3207..c23e879 100644 --- a/docs/openapi/rewards.swagger.json +++ b/docs/openapi/rewards.swagger.json @@ -409,7 +409,7 @@ }, "date": { "type": "string", - "title": "The date of the reward in format 'YYYY-MM-DD' in UTC.", + "description": "The date of the reward in format 'YYYY-MM-DD' in UTC.", "readOnly": true }, "aggregationUnit": { @@ -454,7 +454,7 @@ "readOnly": true } }, - "title": "Rewards earned within a particular period of time." + "description": "Rewards earned within a particular period of time." }, "v1RewardRate": { "type": "object", @@ -537,7 +537,7 @@ "readOnly": true } }, - "title": "The representation of a staking activity at a particular point in time." + "description": "The representation of a staking activity at a particular point in time." }, "v1USDValue": { "type": "object", diff --git a/examples/ethereum/create-and-process-workflow.ts b/examples/ethereum/create-and-process-workflow.ts index 346f7ef..e58fe4e 100644 --- a/examples/ethereum/create-and-process-workflow.ts +++ b/examples/ethereum/create-and-process-workflow.ts @@ -11,7 +11,6 @@ import { calculateTimeDifference } from '../../src/utils/date'; const privateKey: string = ''; // replace with your private key const stakerAddress: string = '0xdb816889F2a7362EF242E5a717dfD5B38Ae849FE'; // replace with your staker address -const integrationAddress: string = '0xA55416de5DE61A0AC1aa8970a280E04388B1dE4b'; // replace with your integration address const amount: string = '123'; // replace with your amount const network: string = 'holesky'; // replace with your network @@ -33,12 +32,7 @@ async function stakePartialEth(): Promise { try { // Create a new eth kiln stake workflow - workflow = await client.Ethereum.stake( - network, - stakerAddress, - integrationAddress, - amount, - ); + workflow = await client.Ethereum.stake(network, stakerAddress, amount); workflowId = workflow.name?.split('/').pop() || ''; if (workflowId == null || workflowId === '') { diff --git a/examples/ethereum/create-workflow.ts b/examples/ethereum/create-workflow.ts index a8adf7f..ef80354 100644 --- a/examples/ethereum/create-workflow.ts +++ b/examples/ethereum/create-workflow.ts @@ -2,7 +2,6 @@ import { StakingClient } from '../../src/client/staking-client'; import { Workflow } from '../../src/gen/coinbase/staking/orchestration/v1/workflow.pb'; const stakerAddress: string = '0xdb816889F2a7362EF242E5a717dfD5B38Ae849FE'; // replace with your staker address -const integrationAddress: string = '0xA55416de5DE61A0AC1aa8970a280E04388B1dE4b'; // replace with your integration address const amount: string = '123'; // replace with your amount const network: string = 'holesky'; // replace with your network @@ -17,14 +16,9 @@ async function stakePartialEth(): Promise { try { // Create a new eth kiln stake workflow - workflow = await client.Ethereum.stake( - network, - stakerAddress, - integrationAddress, - amount, - ); - - console.log('Workflow created %s ...', workflow.name); + workflow = await client.Ethereum.stake(network, stakerAddress, amount); + + console.log(JSON.stringify(workflow, null, 2)); } catch (error) { let errorMessage = ''; diff --git a/examples/solana/create-and-process-workflow.ts b/examples/solana/create-and-process-workflow.ts index d62a1a7..0e8e7ed 100644 --- a/examples/solana/create-and-process-workflow.ts +++ b/examples/solana/create-and-process-workflow.ts @@ -11,7 +11,6 @@ import { calculateTimeDifference } from '../../src/utils/date'; const privateKey: string = ''; // replace with your private key const walletAddress: string = ''; // replace with your wallet address -const validatorAddress: string = 'beefKGBWeSpHzYBHZXwp5So7wdQGX6mu4ZHCsH3uTar'; // replace with your validator address const amount: string = '100000000'; // replace with your amount. For solana it should be >= 0.1 SOL const network: string = 'mainnet'; // replace with your network @@ -33,12 +32,7 @@ async function stakeSolana(): Promise { try { // Create a new solana stake workflow - workflow = await client.Solana.stake( - network, - walletAddress, - validatorAddress, - amount, - ); + workflow = await client.Solana.stake(network, walletAddress, amount); workflowId = workflow.name?.split('/').pop() || ''; if (workflowId == null || workflowId === '') { diff --git a/examples/solana/create-workflow.ts b/examples/solana/create-workflow.ts index 11dd9d9..2cb97bb 100644 --- a/examples/solana/create-workflow.ts +++ b/examples/solana/create-workflow.ts @@ -1,8 +1,7 @@ import { StakingClient } from '../../src/client/staking-client'; import { Workflow } from '../../src/gen/coinbase/staking/orchestration/v1/workflow.pb'; -const walletAddress: string = ''; // replace with your wallet address -const validatorAddress: string = 'beefKGBWeSpHzYBHZXwp5So7wdQGX6mu4ZHCsH3uTar'; // replace with your validator address +const walletAddress: string = '9NL2SkpcsdyZwsG8NmHGNra4i4NSyKbJTVd9fUQ7kJHR'; // replace with your wallet address const amount: string = '100000000'; // replace with your amount. For solana it should be >= 0.1 SOL const network: string = 'mainnet'; // replace with your network @@ -17,14 +16,9 @@ async function stakeSolana(): Promise { try { // Create a new solana stake workflow - workflow = await client.Solana.stake( - network, - walletAddress, - validatorAddress, - amount, - ); - - console.log('Workflow created %s ...', workflow.name); + workflow = await client.Solana.stake(network, walletAddress, amount); + + console.log(JSON.stringify(workflow, null, 2)); } catch (error) { let errorMessage = ''; diff --git a/scripts/generate-client.sh b/scripts/generate-client.sh index a430fdb..9d19851 100755 --- a/scripts/generate-client.sh +++ b/scripts/generate-client.sh @@ -3,4 +3,4 @@ buf generate --template protos/buf.gen.orchestration.yaml buf.build/cdp/orchestration --path coinbase/staking/orchestration/v1 --include-imports --include-wkt buf generate --template protos/buf.gen.rewards.yaml buf.build/cdp/rewards --path coinbase/staking/rewards/v1 --include-imports --include-wkt # TODO: Remove this once the generation issue is fixed. -find ./src/gen -type f -exec sed -I '' -e 's/parentprotocols/parent/g' -e 's/parentprotocolsnetworks/parents/g' -e 's/parentnetworks/parent/g' -e 's/nameprojectsworkflows/name/g' -e 's/parentprojects/parent/g' -e 's/nameworkflows/name/g' {} \; +find ./src/gen -type f -exec sed -I '' -e 's/parentprotocols/parent/g' -e 's/parentprotocolsnetworks/parents/g' -e 's/parentnetworks/parent/g' -e 's/nameprojectsworkflows/name/g' -e 's/nameworkflows/name/g' {} \; diff --git a/src/client/protocols/ethereum-kiln-staking.ts b/src/client/protocols/ethereum-kiln-staking.ts index 756cb17..34fb17e 100644 --- a/src/client/protocols/ethereum-kiln-staking.ts +++ b/src/client/protocols/ethereum-kiln-staking.ts @@ -26,8 +26,8 @@ export class Ethereum { async stake( network: string, stakerAddress: string, - integratorContractAddress: string, amount: string, + integratorContractAddress?: string, ): Promise { const req: CreateWorkflowRequest = { workflow: { @@ -51,8 +51,8 @@ export class Ethereum { async unstake( network: string, stakerAddress: string, - integratorContractAddress: string, amount: string, + integratorContractAddress?: string, ): Promise { const req: CreateWorkflowRequest = { workflow: { @@ -76,7 +76,7 @@ export class Ethereum { async claimStake( network: string, stakerAddress: string, - integratorContractAddress: string, + integratorContractAddress?: string, ): Promise { const req: CreateWorkflowRequest = { workflow: { diff --git a/src/client/protocols/solana-staking.ts b/src/client/protocols/solana-staking.ts index 94768d0..f7e73f8 100644 --- a/src/client/protocols/solana-staking.ts +++ b/src/client/protocols/solana-staking.ts @@ -26,8 +26,8 @@ export class Solana { async stake( network: string, walletAddress: string, - validatorAddress: string, amount: string, + validatorAddress?: string, ): Promise { const req: CreateWorkflowRequest = { workflow: { diff --git a/src/client/staking-client.ts b/src/client/staking-client.ts index 6435cba..eb5f53c 100644 --- a/src/client/staking-client.ts +++ b/src/client/staking-client.ts @@ -130,7 +130,7 @@ export class StakingClient { return StakingService.ViewStakingContext(req, initReq); } - // Create a workflow under a given project. This function takes the entire req object as input. + // Create a workflow. This function takes the entire req object as input. // Use the protocol-specific helper functions like Ethereum.Stake to create a protocol and action specific workflow. async createWorkflow(req: CreateWorkflowRequest): Promise { const path: string = `/v1/workflows`; @@ -143,7 +143,7 @@ export class StakingClient { return StakingService.CreateWorkflow(req, initReq); } - // Get a workflow given its project and workflow id. + // Get a workflow given workflow id. async getWorkflow(workflowId: string): Promise { const name: string = `workflows/${workflowId}`; const path: string = `/v1/${name}`; @@ -183,7 +183,7 @@ export class StakingClient { return StakingService.PerformWorkflowStep(req, initReq); } - // List workflows for a given project. + // List your workflows. async listWorkflows( pageSize: number = 100, filter: string = '', diff --git a/src/gen/coinbase/staking/orchestration/v1/workflow.pb.ts b/src/gen/coinbase/staking/orchestration/v1/workflow.pb.ts index 899e1e7..d0c36f1 100644 --- a/src/gen/coinbase/staking/orchestration/v1/workflow.pb.ts +++ b/src/gen/coinbase/staking/orchestration/v1/workflow.pb.ts @@ -54,6 +54,13 @@ export enum ProvisionInfraStepOutputState { STATE_FAILED = "STATE_FAILED", } +export enum BulkTxStepOutputState { + STATE_UNSPECIFIED = "STATE_UNSPECIFIED", + STATE_IN_PROGRESS = "STATE_IN_PROGRESS", + STATE_FAILED = "STATE_FAILED", + STATE_COMPLETED = "STATE_COMPLETED", +} + export enum WorkflowState { STATE_UNSPECIFIED = "STATE_UNSPECIFIED", STATE_IN_PROGRESS = "STATE_IN_PROGRESS", @@ -82,13 +89,18 @@ export type ProvisionInfraStepOutput = { state?: ProvisionInfraStepOutputState } +export type BulkTxStepOutput = { + unsignedTxs?: string[] + state?: BulkTxStepOutputState +} + type BaseWorkflowStep = { name?: string } export type WorkflowStep = BaseWorkflowStep - & OneOf<{ txStepOutput: TxStepOutput; waitStepOutput: WaitStepOutput; provisionInfraStepOutput: ProvisionInfraStepOutput }> + & OneOf<{ txStepOutput: TxStepOutput; waitStepOutput: WaitStepOutput; provisionInfraStepOutput: ProvisionInfraStepOutput; bulkTxStepOutput: BulkTxStepOutput }> type BaseWorkflow = {