From ab79ee1fa3e69ab3f926bc258044c1d1e1c9942c Mon Sep 17 00:00:00 2001 From: Alexandru Stefan Date: Wed, 20 Sep 2023 15:22:11 +0300 Subject: [PATCH] feat: add support for using Marinade Native in the SDK --- package.json | 11 +-- pnpm-lock.yaml | 31 +++++++- src/marinade-native-stake.ts | 121 +++++++++++++++++++++++++++++ src/marinade-native-stake.types.ts | 11 +++ tsconfig.json | 2 +- 5 files changed, 167 insertions(+), 9 deletions(-) create mode 100644 src/marinade-native-stake.ts create mode 100644 src/marinade-native-stake.types.ts diff --git a/package.json b/package.json index df7cfcd..2d1dc35 100644 --- a/package.json +++ b/package.json @@ -37,24 +37,28 @@ "dependencies": { "@coral-xyz/anchor": "^0.28.0", "@marinade.finance/directed-stake-sdk": "^0.0.4", + "@marinade.finance/native-staking-sdk": "^1.0.0", "@solana/spl-stake-pool": "^0.6.5", "@solana/spl-token-3.x": "npm:@solana/spl-token@^0.3.8", "borsh": "^0.7.0", "bs58": "^5.0.0" }, "devDependencies": { + "@jest/globals": "^29.5.0", + "@solana/web3.js": "^1.74.0", "@types/bn.js": "^5.1.1", "@types/bs58": "^4.0.1", "@types/jest": "^29.5.1", - "@jest/globals": "^29.5.0", "@types/node": "^18.16.3", "@typescript-eslint/eslint-plugin": "^5.59.1", "@typescript-eslint/parser": "^5.59.1", + "bn.js": "^5.2.1", "eslint": "^8.39.0", "gts": "^3.1.1", "husky": "^8.0.3", "jest": "^29.5.0", "jest-each": "^29.5.0", + "jsbi": "^4.3.0", "lint-staged": "^13.2.2", "node-polyfill-webpack-plugin": "^2.0.1", "terser-webpack-plugin": "^5.3.7", @@ -63,10 +67,7 @@ "ts-node": "^10.9.1", "typescript": "^5.0.4", "webpack": "^5.81.0", - "webpack-cli": "^5.0.2", - "@solana/web3.js": "^1.74.0", - "bn.js": "^5.2.1", - "jsbi": "^4.3.0" + "webpack-cli": "^5.0.2" }, "engines": { "node": ">=16.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6441fbc..c11d1dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,9 @@ dependencies: '@marinade.finance/directed-stake-sdk': specifier: ^0.0.4 version: 0.0.4(@solana/web3.js@1.75.0)(bn.js@5.2.1)(jsbi@4.3.0) + '@marinade.finance/native-staking-sdk': + specifier: ^1.0.0 + version: 1.0.0 '@solana/spl-stake-pool': specifier: ^0.6.5 version: 0.6.5 @@ -77,7 +80,7 @@ devDependencies: version: 5.3.7(webpack@5.81.0) ts-jest: specifier: ^29.1.0 - version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) + version: 29.1.0(jest@29.5.0)(typescript@5.0.4) ts-loader: specifier: ^9.4.2 version: 9.4.2(typescript@5.0.4)(webpack@5.81.0) @@ -917,6 +920,19 @@ packages: - utf-8-validate dev: false + /@marinade.finance/native-staking-sdk@1.0.0: + resolution: {integrity: sha512-Cj2dy3SH9LAgcFBpGEgRHyF/nK+L7SfNIHkGqAZYRDVgn/h7u8bqKNkYqKXgCWbA5WwPmJzugWWm1DxEt7LyPQ==} + dependencies: + '@solana/spl-memo': 0.2.3(@solana/web3.js@1.75.0) + '@solana/web3.js': 1.75.0 + bn.js: 5.2.1 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: false + /@noble/ed25519@1.7.3: resolution: {integrity: sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==} @@ -995,6 +1011,16 @@ packages: dependencies: buffer: 6.0.3 + /@solana/spl-memo@0.2.3(@solana/web3.js@1.75.0): + resolution: {integrity: sha512-CNsKSsl85ebuVoeGq1LDYi5M/PMs1Pxv2/UsyTgS6b30qrYqZOXha5ouZzgGKtJtZ3C3dxfOAEw6caJPN1N63w==} + engines: {node: '>=16'} + peerDependencies: + '@solana/web3.js': ^1.20.0 + dependencies: + '@solana/web3.js': 1.75.0 + buffer: 6.0.3 + dev: false + /@solana/spl-stake-pool@0.6.5: resolution: {integrity: sha512-gAYjX4LlRem3Bje1csZOOBStX8wAH8b8tu4sublUTIoJxLMdEbXqnwc8RJ2lAsmFkjxxomEM9Hk65F8jcvv15A==} dependencies: @@ -5296,7 +5322,7 @@ packages: engines: {node: '>=8'} dev: true - /ts-jest@29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4): + /ts-jest@29.1.0(jest@29.5.0)(typescript@5.0.4): resolution: {integrity: sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -5317,7 +5343,6 @@ packages: esbuild: optional: true dependencies: - '@babel/core': 7.21.4 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 jest: 29.5.0(@types/node@18.16.3)(ts-node@10.9.1) diff --git a/src/marinade-native-stake.ts b/src/marinade-native-stake.ts new file mode 100644 index 0000000..5994165 --- /dev/null +++ b/src/marinade-native-stake.ts @@ -0,0 +1,121 @@ +import { + PublicKey, + TransactionInstruction, + VersionedTransaction, +} from '@solana/web3.js' +import BN from 'bn.js' +import { NativeStakingSDK } from '@marinade.finance/native-staking-sdk' +import { + AuthStakeSOLIxResponse, + UnstakeSOLIxResponse, +} from './marinade-native-stake.types' + +/** + * Fetches the instruction to stake SOL in Marinade Native + * + * @param {PublicKey} userPublicKey - The PublicKey of the user + * @param {BN} amountToStake - The amount to deposit in lamports + * @returns {AuthStakeSOLIxResponse} - The instructions to add in transaction to stake in Marinade Native and new stake keypair + */ +export function getAuthNativeStakeSOLIx({ + userPublicKey, + amountToStake, +}: { + userPublicKey: PublicKey + amountToStake: BN +}): AuthStakeSOLIxResponse { + const nativeSdk = new NativeStakingSDK() + return nativeSdk.buildCreateAuthorizedStakeInstructions( + userPublicKey, + amountToStake + ) +} + +/** + * Fetches the transaction to stake SOL in Marinade Native with refCode + * + * @param {PublicKey} userPublicKey - The PublicKey of the user + * @param {BN} amountToStake - The amount to deposit in lamports + * @param {string} mNativeRefCode - Marinade Native referral code + * @returns {Promise} - The transaction to stake SOL into Marinade native using Referral Code + */ +export async function getRefNativeStakeSOLTx({ + userPublicKey, + amountToStake, + mNativeRefCode, +}: { + userPublicKey: PublicKey + amountToStake: BN + mNativeRefCode: string +}): Promise { + const response = await fetch( + `https://native-staking-referral.marinade.finance/v1/tx/deposit-sol?amount=${amountToStake.toString()}&code=${mNativeRefCode}&user=${userPublicKey.toString()}` + ) + const serializedTx = await response.json() + + const txBuffer = Buffer.from(serializedTx, 'base64') + return VersionedTransaction.deserialize(txBuffer) +} + +/** + * Fetches the instruction to stake Stake Account in Marinade Native + * + * @param {PublicKey} userPublicKey - The PublicKey of the user + * @param {PublicKey[]} stakeAccounts - The stake accounts that are about to be deposited in Marinade Native + * @returns {TransactionInstruction} - The instruction to add in transaction to stake in Marinade Native + */ +export function getAuthNativeStakeAccountIx({ + userPublicKey, + stakeAccounts, +}: { + userPublicKey: PublicKey + stakeAccounts: PublicKey[] +}): TransactionInstruction[] { + const nativeSdk = new NativeStakingSDK() + return nativeSdk.buildAuthorizeInstructions(userPublicKey, stakeAccounts) +} + +/** + * Fetches the transaction to deposit Stake Account in Marinade Native with refCode + * + * @param {PublicKey} userPublicKey - The PublicKey of the user + * @param {PublicKey} stakeAccountAddress - The stake account to be deposited into Marinade Native + * @param {string} mNativeRefCode - Marinade Native referral code + * @returns {Promise} - The transaction to deposit Stake Account into Marinade native using Referral Code + */ +export async function getRefNativeStakeAccountTx({ + userPublicKey, + stakeAccountAddress, + mNativeRefCode, +}: { + userPublicKey: PublicKey + stakeAccountAddress: PublicKey + mNativeRefCode: string +}): Promise { + const response = await fetch( + `https://native-staking-referral.marinade.finance/v1/tx/deposit-stake-account?stake=${stakeAccountAddress.toString()}&code=${mNativeRefCode}&user=${userPublicKey.toString()}` + ) + + const serializedTx = await response.json() + + const txBuffer = Buffer.from(serializedTx, 'base64') + return VersionedTransaction.deserialize(txBuffer) +} + +/** + * Fetches the instruction to unstake from Marinade Native + * + * @param {PublicKey} userPublicKey - The PublicKey of the user + * @param {BN} amountToUnstake - The amount to unstake in lamports + * @returns {UnstakeSOLIxResponse} - The instructions to pay the fee for unstake and the event that should be called after fee is paid in order to trigger unstake event faster (calling this is optional, but it ensures better experience for user). + */ +export async function getNativeUnstakeSOLIx({ + userPublicKey, + amountToUnstake, +}: { + userPublicKey: PublicKey + amountToUnstake: BN +}): Promise { + const nativeSdk = new NativeStakingSDK() + return await nativeSdk.initPrepareForRevoke(userPublicKey, amountToUnstake) +} diff --git a/src/marinade-native-stake.types.ts b/src/marinade-native-stake.types.ts new file mode 100644 index 0000000..f244566 --- /dev/null +++ b/src/marinade-native-stake.types.ts @@ -0,0 +1,11 @@ +import { Keypair, TransactionInstruction } from '@solana/web3.js' + +export type AuthStakeSOLIxResponse = { + createAuthorizedStake: TransactionInstruction[] + stakeKeypair: Keypair +} + +export type UnstakeSOLIxResponse = { + payFees: TransactionInstruction[] + onPaid: (signature: string) => Promise +} diff --git a/tsconfig.json b/tsconfig.json index 1a961ae..2368ecb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { - "lib": ["es2021"], + "lib": ["es2021", "dom"], "target": "ES6", "module": "CommonJS", "rootDir": ".",