Skip to content

Commit

Permalink
Merge pull request #14 from coinbase/main-1712589815
Browse files Browse the repository at this point in the history
Release v.0.5.1
  • Loading branch information
teddylo-cb authored Apr 8, 2024
2 parents f88c0a2 + c7cdb7c commit 2ba88d0
Show file tree
Hide file tree
Showing 11 changed files with 825 additions and 154 deletions.
14 changes: 14 additions & 0 deletions examples/cosmos/list-rewards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { StakingClient } from '../../src/client/staking-client';

// Defines which address and rewards we want to see
const address: string = 'cosmosvaloper1c4k24jzduc365kywrsvf5ujz4ya6mwympnc4en';
const filter: string = `address='${address}' AND period_end_time > '2024-03-25T00:00:00Z' AND period_end_time < '2024-03-27T00:00:00Z'`;

const client = new StakingClient();

// Loops through rewards array and prints each reward
client.Cosmos.listRewards(filter).then((resp) => {
resp.rewards!.forEach((reward) => {
console.log(JSON.stringify(reward, null, 2));
});
});
37 changes: 37 additions & 0 deletions examples/cosmos/list-stakes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { StakingClient } from '../../src/client/staking-client';

// TODO: Replace address as per your requirement.
const address: string = 'cosmosvaloper1c4k24jzduc365kywrsvf5ujz4ya6mwympnc4en';

const client = new StakingClient();

async function listStakes(): Promise<void> {
if (address === '') {
throw new Error('Please set the address variable in this file');
}

const filter: string = `address='${address}'`;

try {
// List cosmos staking balances
let resp = await client.Cosmos.listStakes(filter);

let count = 0;

// Loop through staked balance array and print each balance
resp.stakes!.forEach((stake) => {
count++;
const marshaledStake = JSON.stringify(stake);

console.log(`[${count}] Stake details: ${marshaledStake}`);
});
} catch (error) {
if (error instanceof Error) {
throw new Error(`Error listing staking balances: ${error.message}`);
}
}
}

listStakes().catch((error) => {
console.error('Error listing cosmos staking balances: ', error.message);
});
4 changes: 2 additions & 2 deletions examples/ethereum/list-stakes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ async function listStakes(): Promise<void> {

let count = 0;

// Loop through rewards array and print each reward
// Loop through staked balance array and print each balance
resp.stakes!.forEach((stake) => {
count++;
const marshaledStake = JSON.stringify(stake);
Expand All @@ -34,5 +34,5 @@ async function listStakes(): Promise<void> {
}

listStakes().catch((error) => {
console.error('Error listing solana staking balances: ', error.message);
console.error('Error listing ethereum staking balances: ', error.message);
});
188 changes: 188 additions & 0 deletions examples/solana/create-and-process-workflow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import { TxSignerFactory } from '../../src/signers';
import {
StakingClient,
workflowHasFinished,
workflowWaitingForSigning,
workflowWaitingForExternalBroadcast,
isTxStepOutput,
isWaitStepOutput,
} from '../../src/client/staking-client';
import { Workflow } from '../../src/gen/coinbase/staking/orchestration/v1/workflow.pb';
import { calculateTimeDifference } from '../../src/utils/date';

const projectId: string = ''; // replace with your project id
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

const client = new StakingClient();

const signer = TxSignerFactory.getSigner('solana');

async function stakeSolana(): Promise<void> {
if (projectId === '' || walletAddress === '') {
throw new Error(
'Please set the projectId and stakerAddress variables in this file',
);
}

let unsignedTx = '';
let workflow: Workflow = {} as Workflow;
let currentStepId: number | undefined;
let workflowId: string;

try {
// Create a new solana stake workflow
workflow = await client.Solana.stake(
projectId,
network,
true,
walletAddress,
validatorAddress,
amount,
);

workflowId = workflow.name?.split('/').pop() || '';
if (workflowId == null || workflowId === '') {
throw new Error('Unexpected workflow state. workflowId is null');
}

currentStepId = workflow.currentStepId;
if (currentStepId == null) {
throw new Error('Unexpected workflow state. currentStepId is null');
}

console.log('Workflow created %s ...', workflow.name);
} catch (error) {
if (error instanceof Error) {
throw new Error(`Error creating workflow: ${error.message}`);
}

const msg = JSON.stringify(error);

throw new Error(`Error creating workflow ${msg}`);
}

// Loop until the workflow has reached an end state.
// eslint-disable-next-line no-constant-condition
while (true) {
// Every second, get the latest workflow state.
// If the workflow is waiting for signing, sign the unsigned tx and return back the signed tx.
// If the workflow is waiting for external broadcast, sign and broadcast the unsigned tx externally and return back the tx hash via the PerformWorkflowStep API.
// Note: In this example, we just log this message as the wallet provider needs to implement this logic.
try {
workflow = await client.getWorkflow(projectId, workflowId);
} catch (error) {
// TODO: add retry logic for network errors
if (error instanceof Error) {
throw new Error(`Error getting workflow: ${error.message}`);
}
}

await printWorkflowProgressDetails(workflow);

if (workflowWaitingForSigning(workflow)) {
unsignedTx =
workflow.steps![currentStepId].txStepOutput?.unsignedTx || '';
if (unsignedTx === '') {
console.log('Waiting for unsigned tx to be available ...');
await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep for 1 second
continue;
}

console.log('Signing unsigned tx %s ...', unsignedTx);
const signedTx = await signer.signTransaction(privateKey, unsignedTx);

console.log('Returning back signed tx %s ...', signedTx);

workflow = await client.performWorkflowStep(
projectId,
workflowId,
currentStepId,
signedTx,
);
} else if (workflowWaitingForExternalBroadcast(workflow)) {
unsignedTx =
workflow.steps![currentStepId].txStepOutput?.unsignedTx || '';
if (unsignedTx === '') {
console.log('Waiting for unsigned tx to be available ...');
await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep for 1 second
continue;
}

console.log('Signing unsigned tx %s ...', unsignedTx);

const signedTx = await signer.signTransaction(privateKey, unsignedTx);

console.log(
'Please broadcast this signed tx %s externally and return back the tx hash via the PerformWorkflowStep API ...',
signedTx,
);
break;
} else if (workflowHasFinished(workflow)) {
console.log('Workflow completed with state %s ...', workflow.state);
break;
}

await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep for 1 second
}
}

async function printWorkflowProgressDetails(workflow: Workflow): Promise<void> {
if (workflow.steps == null || workflow.steps.length === 0) {
console.log('Waiting for steps to be created ...');
await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep for 1 second
return;
}

const currentStepId = workflow.currentStepId;

if (currentStepId == null) {
return;
}

const step = workflow.steps[currentStepId];

let stepDetails = '';

if (isTxStepOutput(step)) {
stepDetails = `state: ${step.txStepOutput?.state} tx hash: ${step.txStepOutput?.txHash}`;
} else if (isWaitStepOutput(step)) {
stepDetails = `state: ${step.waitStepOutput?.state}} current: ${step.waitStepOutput?.current}} target: ${step.waitStepOutput?.target}`;
} else {
throw new Error('Encountered unexpected workflow step type');
}

const runtime = calculateTimeDifference(
<string>workflow.createTime,
<string>workflow.updateTime,
);

if (workflowHasFinished(workflow)) {
console.log(
'Workflow reached end state - step name: %s %s workflow state: %s runtime: %d seconds',
step.name,
stepDetails,
workflow.state,
runtime,
);
} else {
console.log(
'Waiting for workflow to finish - step name: %s %s workflow state: %s runtime: %d seconds',
step.name,
stepDetails,
workflow.state,
runtime,
);
}
}

stakeSolana()
.then(() => {
console.log('Done staking sol');
})
.catch((error) => {
console.error('Error staking sol: ', error.message);
});
Loading

0 comments on commit 2ba88d0

Please sign in to comment.