diff --git a/examples/cosmos/list-rewards.ts b/examples/cosmos/list-rewards.ts new file mode 100644 index 0000000..2be233c --- /dev/null +++ b/examples/cosmos/list-rewards.ts @@ -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)); + }); +}); diff --git a/examples/cosmos/list-stakes.ts b/examples/cosmos/list-stakes.ts new file mode 100644 index 0000000..e51903b --- /dev/null +++ b/examples/cosmos/list-stakes.ts @@ -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 { + 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); +}); diff --git a/examples/ethereum/list-stakes.ts b/examples/ethereum/list-stakes.ts index 8835d32..1c9ee0d 100644 --- a/examples/ethereum/list-stakes.ts +++ b/examples/ethereum/list-stakes.ts @@ -19,7 +19,7 @@ async function listStakes(): Promise { 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); @@ -34,5 +34,5 @@ async function listStakes(): Promise { } listStakes().catch((error) => { - console.error('Error listing solana staking balances: ', error.message); + console.error('Error listing ethereum staking balances: ', error.message); }); diff --git a/examples/solana/create-and-process-workflow.ts b/examples/solana/create-and-process-workflow.ts new file mode 100644 index 0000000..31632a1 --- /dev/null +++ b/examples/solana/create-and-process-workflow.ts @@ -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 { + 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 { + 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( + workflow.createTime, + 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); + }); diff --git a/examples/solana/create-workflow.ts b/examples/solana/create-workflow.ts index 72898c1..d38d807 100644 --- a/examples/solana/create-workflow.ts +++ b/examples/solana/create-workflow.ts @@ -1,13 +1,5 @@ -import { - StakingClient, - workflowHasFinished, - workflowWaitingForSigning, - workflowWaitingForExternalBroadcast, - isTxStepOutput, - isWaitStepOutput, -} from '../../src/client/staking-client'; +import { StakingClient } 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 walletAddress: string = ''; // replace with your wallet address @@ -24,10 +16,7 @@ async function stakeSolana(): Promise { ); } - let unsignedTx = ''; let workflow: Workflow = {} as Workflow; - let currentStepId: number | undefined; - let workflowId: string; try { // Create a new solana stake workflow @@ -40,143 +29,23 @@ async function stakeSolana(): Promise { 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}`); - } - throw new Error(`Error creating workflow`); - } - - // 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}`); - } - } + let errorMessage = ''; - await printWorkflowProgressDetails(workflow); - - if (workflowWaitingForSigning(workflow)) { - console.log( - 'Please sign this unsigned tx %s and return back the signed tx via the PerformWorkflowStep API ...', - unsignedTx, - ); - - /* This is where a customer needs to implement signing logic and return back signedTx - workflow = await client.performWorkflowStep( - projectId, - workflowId, - currentStepId, - signedTx, - ); - */ - break; - } 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( - 'Please sign and broadcast this unsigned tx %s externally and return back the tx hash via the PerformWorkflowStep API ...', - unsignedTx, - ); - - /* This is where a customer needs to implement signing, broadcasting logic and return back broadcasted tx hash - workflow = await client.performWorkflowStep( - projectId, - workflowId, - currentStepId, - txHash, - ); - */ - - break; - } else if (workflowHasFinished(workflow)) { - console.log('Workflow completed with state %s ...', workflow.state); - break; + if (error instanceof Error) { + errorMessage = error.message; } - - await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep for 1 second - } -} - -async function printWorkflowProgressDetails(workflow: Workflow): Promise { - 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( - workflow.createTime, - 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, - ); + throw new Error(`Error creating workflow: ${errorMessage}`); } } stakeSolana() .then(() => { - console.log('Done staking sol'); + console.log('Done creating sol staking workflow'); }) .catch((error) => { - console.error('Error staking sol: ', error.message); + if (error instanceof Error) { + console.error('Error creating sol staking workflow: ', error.message); + } }); diff --git a/package-lock.json b/package-lock.json index 849061f..079e0be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,17 @@ { "name": "@coinbase/staking-client-library-ts", - "version": "0.5.0", + "version": "0.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@coinbase/staking-client-library-ts", - "version": "0.5.0", + "version": "0.5.1", "license": "Apache-2.0", "dependencies": { "@ethereumjs/tx": "^5.1.0", + "@solana/web3.js": "^1.91.3", + "bs58": "^5.0.0", "node-jose": "^2.2.0" }, "devDependencies": { @@ -146,6 +148,17 @@ "node": ">=4" } }, + "node_modules/@babel/runtime": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", + "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -417,6 +430,85 @@ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, + "node_modules/@solana/buffer-layout": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz", + "integrity": "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==", + "dependencies": { + "buffer": "~6.0.3" + }, + "engines": { + "node": ">=5.10" + } + }, + "node_modules/@solana/web3.js": { + "version": "1.91.3", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.91.3.tgz", + "integrity": "sha512-Z6FZyW8SWm7RXW5ZSyr1kmpR+eH/F4DhgxV4WPaq5AbAAMnCiiGm36Jb7ACHFXtWzq1a24hBkJ1wnVANjsmdPA==", + "dependencies": { + "@babel/runtime": "^7.23.4", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.3", + "@solana/buffer-layout": "^4.0.1", + "agentkeepalive": "^4.5.0", + "bigint-buffer": "^1.1.5", + "bn.js": "^5.2.1", + "borsh": "^0.7.0", + "bs58": "^4.0.1", + "buffer": "6.0.3", + "fast-stable-stringify": "^1.0.0", + "jayson": "^4.1.0", + "node-fetch": "^2.7.0", + "rpc-websockets": "^7.5.1", + "superstruct": "^0.14.2" + } + }, + "node_modules/@solana/web3.js/node_modules/@noble/curves": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", + "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@solana/web3.js/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@solana/web3.js/node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@solana/web3.js/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -427,7 +519,6 @@ "version": "20.11.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", - "dev": true, "dependencies": { "undici-types": "~5.26.4" } @@ -453,6 +544,14 @@ "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", "dev": true }, + "node_modules/@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.0.tgz", @@ -768,6 +867,17 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -829,6 +939,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base-x": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==" + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -856,6 +971,57 @@ "node": ">=6.0.0" } }, + "node_modules/bigint-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", + "integrity": "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.3.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/borsh": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz", + "integrity": "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==", + "dependencies": { + "bn.js": "^5.2.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + }, + "node_modules/borsh/node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/borsh/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -910,6 +1076,14 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs58": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "dependencies": { + "base-x": "^4.0.0" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -933,6 +1107,19 @@ "ieee754": "^1.2.1" } }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/builtin-modules": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", @@ -1044,6 +1231,11 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "node_modules/common-tags": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", @@ -1125,6 +1317,17 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1175,6 +1378,14 @@ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -1505,6 +1716,19 @@ "@scure/bip39": "1.2.1" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "engines": { + "node": "> 0.1.90" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1557,6 +1781,11 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-stable-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz", + "integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==" + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -1578,6 +1807,11 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1762,6 +1996,14 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -1918,6 +2160,52 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/jayson": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz", + "integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==", + "dependencies": { + "@types/connect": "^3.4.33", + "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", + "commander": "^2.20.3", + "delay": "^5.0.0", + "es6-promisify": "^5.0.0", + "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", + "json-stringify-safe": "^5.0.1", + "JSONStream": "^1.3.5", + "uuid": "^8.3.2", + "ws": "^7.4.5" + }, + "bin": { + "jayson": "bin/jayson.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jayson/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" + }, + "node_modules/jayson/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1972,6 +2260,34 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/keyv": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", @@ -2176,8 +2492,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -2185,6 +2500,25 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -2193,6 +2527,17 @@ "node": ">= 6.13.0" } }, + "node_modules/node-gyp-build": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-jose": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/node-jose/-/node-jose-2.2.0.tgz", @@ -2647,6 +2992,11 @@ "node": ">=8" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, "node_modules/regexp-tree": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", @@ -2746,6 +3096,53 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rpc-websockets": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.9.0.tgz", + "integrity": "sha512-DwKewQz1IUA5wfLvgM8wDpPRcr+nWSxuFxx5CbrI2z/MyyZ4nXLM86TvIA+cI1ZAdqC8JIBR1mZR55dzaLU+Hw==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "eventemitter3": "^4.0.7", + "uuid": "^8.3.2", + "ws": "^8.5.0" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/kozjak" + }, + "optionalDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + } + }, + "node_modules/rpc-websockets/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/rpc-websockets/node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2769,6 +3166,25 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -2882,6 +3298,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/superstruct": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.14.2.tgz", + "integrity": "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2922,12 +3343,22 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/text-encoding-utf-8": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz", + "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2940,6 +3371,11 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/ts-api-utils": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", @@ -2998,8 +3434,7 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/update-browserslist-db": { "version": "1.0.13", @@ -3040,6 +3475,19 @@ "punycode": "^2.1.0" } }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/uuid": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", @@ -3082,6 +3530,20 @@ "eslint": ">=6.0.0" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3103,6 +3565,26 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index 11c12a0..cd46379 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@coinbase/staking-client-library-ts", - "version": "0.5.0", + "version": "0.5.1", "description": "Coinbase Staking API Typescript Library", "repository": "https://github.com/coinbase/staking-client-library-ts.git", "license": "Apache-2.0", @@ -20,8 +20,10 @@ "dist/" ], "dependencies": { - "node-jose": "^2.2.0", - "@ethereumjs/tx": "^5.1.0" + "@ethereumjs/tx": "^5.1.0", + "@solana/web3.js": "^1.91.3", + "bs58": "^5.0.0", + "node-jose": "^2.2.0" }, "devDependencies": { "@types/node": "^20.11.0", @@ -36,8 +38,8 @@ "eslint-plugin-unicorn": "^51.0.1", "prettier": "^3.0.3", "prettier-eslint": "^16.0.0", - "typescript": "^5.2.2", - "rimraf": "^3.0.2" + "rimraf": "^3.0.2", + "typescript": "^5.2.2" }, "main": "dist/index.js", "types": "dist/index.d.ts" diff --git a/src/client/protocols/cosmos-staking.ts b/src/client/protocols/cosmos-staking.ts new file mode 100644 index 0000000..d1f9197 --- /dev/null +++ b/src/client/protocols/cosmos-staking.ts @@ -0,0 +1,47 @@ +import { StakingClient } from '../staking-client'; +import { + ListRewardsRequest, + ListRewardsResponse, +} from '../../gen/coinbase/staking/rewards/v1/reward.pb'; +import { + ListStakesRequest, + ListStakesResponse, +} from '../../gen/coinbase/staking/rewards/v1/stake.pb'; + +export class Cosmos { + private parent: StakingClient; + + constructor(parent: StakingClient) { + this.parent = parent; + } + + async listRewards( + filter: string, + pageSize: number = 100, + pageToken?: string, + ): Promise { + const req: ListRewardsRequest = { + parent: 'protocols/cosmos', + filter: filter, + pageSize: pageSize, + pageToken: pageToken, + }; + + return this.parent.listRewards('cosmos', req); + } + + async listStakes( + filter: string, + pageSize: number = 100, + pageToken?: string, + ): Promise { + const req: ListStakesRequest = { + parent: 'protocols/cosmos', + filter: filter, + pageSize: pageSize, + pageToken: pageToken, + }; + + return this.parent.listStakes('cosmos', req); + } +} diff --git a/src/client/staking-client.ts b/src/client/staking-client.ts index e4a1f23..b5579e0 100644 --- a/src/client/staking-client.ts +++ b/src/client/staking-client.ts @@ -41,6 +41,7 @@ import { import { Ethereum } from './protocols/ethereum-kiln-staking'; import { Solana } from './protocols/solana-staking'; +import { Cosmos } from './protocols/cosmos-staking'; const DEFAULT_URL = 'https://api.developer.coinbase.com/staking'; @@ -48,6 +49,7 @@ export class StakingClient { readonly baseURL: string; readonly Ethereum: Ethereum; readonly Solana: Solana; + readonly Cosmos: Cosmos; constructor(baseURL?: string) { if (baseURL) { @@ -58,6 +60,7 @@ export class StakingClient { this.Ethereum = new Ethereum(this); this.Solana = new Solana(this); + this.Cosmos = new Cosmos(this); } // List protocols supported by Staking API diff --git a/src/signers/solana-signer.ts b/src/signers/solana-signer.ts new file mode 100644 index 0000000..d088575 --- /dev/null +++ b/src/signers/solana-signer.ts @@ -0,0 +1,26 @@ +import { Transaction, Account } from '@solana/web3.js'; +import * as bs58 from 'bs58'; +import { TxSigner } from './txsigner'; + +const TRANSACTION_SERIALIZE_CONFIG = { + requireAllSignatures: false, + verifySignatures: true, +}; + +export class SolanaTransactionSigner implements TxSigner { + async signTransaction( + privateKey: string, + unsignedTx: string, + ): Promise { + const txn = bs58.decode(unsignedTx); + const tx = Transaction.from(txn); + + const secretKey = bs58.decode(privateKey); + + const sender = new Account(secretKey); + + tx.sign(sender); + + return bs58.encode(tx.serialize(TRANSACTION_SERIALIZE_CONFIG)); + } +} diff --git a/src/signers/txsigner.ts b/src/signers/txsigner.ts index 76938c4..e438511 100644 --- a/src/signers/txsigner.ts +++ b/src/signers/txsigner.ts @@ -1,4 +1,5 @@ import { EthereumTransactionSigner } from './ethereum-signer'; +import { SolanaTransactionSigner } from './solana-signer'; export interface TxSigner { // eslint-disable-next-line no-unused-vars @@ -10,6 +11,8 @@ export class TxSignerFactory { switch (protocol) { case 'ethereum': return new EthereumTransactionSigner(); + case 'solana': + return new SolanaTransactionSigner(); // other cases for additional protocols default: throw new Error('Unsupported protocol');