diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c6c582..fe95cae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v5.0.5 + +### Feat: + + - Add support to stake into Marinade Native with or without Referral Code + - Add support to unstake from Marinade Native + ## v5.0.4 ### Fix: diff --git a/README.md b/README.md index 2c43f10..abf79ac 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,47 @@ const { const signature = await provider.send(transaction) ``` +### Marinade Native Staking + +You can now stake assets in Marinade Native through the SDK, either with or without a referral code. + +#### Stake without referral code +If you choose to stake without a referral code, the methods exposed in `marinade-native-stake.ts` serve as wrappers for those already detailed in the [Native Stake SDK](https://www.npmjs.com/package/@marinade.finance/native-staking-sdk). +Please note that staking without a referral code will yield only Transaction Instructions. + +#### Stake with referral code +To acquire a referral code, you'll need to visit the [Marinade dApp](https://marinade.finance/app/earn/) to retrieve it. +Once you have the code, you can input it into the methods described below. Please note that the method returns a Versioned Transaction. + +Stake SOL to Marinade Native +```ts +... +const versionedTransaction = await getRefNativeStakeSOLTx(userPublicKey, amountLamports, refCode) +// sign and send the `transaction` +const signature = await wallet.sendTransaction(unsignedTx, connection) +``` + +Deposit Stake Account to Marinade Native +```ts +... +const versionedTransaction = await getRefNativeStakeAccountTx(userPublicKey, stakeAccountAddress, refCode) +// sign and send the `transaction` +const signature = await wallet.sendTransaction(versionedTransaction, connection) +``` + +#### Prepare for Unstake from Marinade Native +To initiate the process of unstaking, you'll need to merge your stake accounts back into a single account and pay the associated fee (in SOL). To do this, execute the following command: + +```ts +... +const transaction = new Transaction() +const prepareUnstakeIx = await getPrepareNativeUnstakeSOLIx(userPublicKey, amountLamports) +transaction.add(...prepareUnstakeIx.payFees) +// sign and send the `transaction` +const signature = await wallet.sendTransaction(transaction, connection) +await authIx.onPaid() +``` + ### Liquidity pool Add liquidity to the liquidity pool and receive LP tokens: diff --git a/package.json b/package.json index df7cfcd..5d290af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@marinade.finance/marinade-ts-sdk", - "version": "5.0.4", + "version": "5.0.5", "description": "Marinade SDK for Typescript", "main": "dist/src/index.js", "repository": { @@ -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/index.ts b/src/index.ts index e8df7a9..933e04d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ export { Marinade } from './marinade' +export * from './marinade-native-stake' export { MarinadeConfig } from './config/marinade-config' export * as MarinadeBorsh from './marinade-state/borsh/index' export { MarinadeReferralPartnerState } from './marinade-referral-state/marinade-referral-partner-state' diff --git a/src/marinade-native-stake.ts b/src/marinade-native-stake.ts new file mode 100644 index 0000000..dd96473 --- /dev/null +++ b/src/marinade-native-stake.ts @@ -0,0 +1,116 @@ +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' + +/** + * Retrieves the instruction for staking SOL with Marinade Native. + * + * @param {PublicKey} userPublicKey - The PublicKey associated with the user. + * @param {BN} amountToStake - The amount to be staked, in lamports. + * @returns {AuthStakeSOLIxResponse} - The instructions to include in the transaction for staking in Marinade Native, along with the new stake keypair. + */ +export function getAuthNativeStakeSOLIx( + userPublicKey: PublicKey, + amountToStake: BN +): AuthStakeSOLIxResponse { + const nativeSdk = new NativeStakingSDK() + return nativeSdk.buildCreateAuthorizedStakeInstructions( + userPublicKey, + amountToStake + ) +} + +/** + * Retrieves the transaction for staking SOL with Marinade Native using a referral code. + * + * @param {PublicKey} userPublicKey - The PublicKey associated with the user. + * @param {BN} amountToStake - The amount to be staked, specified in lamports. + * @param {string} mNativeRefCode - The Marinade Native referral code. + * @returns {Promise} - A promise that resolves to the transaction for staking SOL with Marinade Native using the referral code. + */ +export async function getRefNativeStakeSOLTx( + 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 result = await response.json() + + const txBuffer = Buffer.from(result.serializedTx, 'base64') + return VersionedTransaction.deserialize(txBuffer) +} + +/** + * Retrieves the instruction for staking a Stake Account with Marinade Native. + * + * @param {PublicKey} userPublicKey - The PublicKey associated with the user. + * @param {PublicKey[]} stakeAccounts - The stake accounts to be used with Marinade Native. + * @returns {TransactionInstruction} - The instruction to include in the transaction for staking the Stake Account with Marinade Native. + */ +export function getAuthNativeStakeAccountIx( + userPublicKey: PublicKey, + stakeAccounts: PublicKey[] +): TransactionInstruction[] { + const nativeSdk = new NativeStakingSDK() + return nativeSdk.buildAuthorizeInstructions(userPublicKey, stakeAccounts) +} + +/** + * Retrieves the transaction for using a Stake Account with Marinade Native via a referral code. + * + * @param {PublicKey} userPublicKey - The PublicKey associated with the user. + * @param {PublicKey} stakeAccountAddress - The address of the stake account intended to use with Marinade Native. + * @param {string} mNativeRefCode - The Marinade Native referral code. + * @returns {Promise} - A promise resolving to the transaction required for using the Stake Account with Marinade Native via the given referral code. + */ +export async function getRefNativeStakeAccountTx( + 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 result = await response.json() + + const txBuffer = Buffer.from(result.serializedTx, 'base64') + return VersionedTransaction.deserialize(txBuffer) +} + +/** + * Retrieves the instruction for unstaking from Marinade Native. + * + * @param {PublicKey} userPublicKey - The PublicKey associated with the user. + * @param {BN} amountToUnstake - The amount to be unstaked, specified in lamports. + * @returns {UnstakeSOLIxResponse} - The instructions for paying the unstaking fee and an optional event that can be called after the fee is paid to expedite the unstaking process (calling this event is optional but enhances the user experience). + */ +export async function getPrepareNativeUnstakeSOLIx( + userPublicKey: PublicKey, + amountToUnstake: BN +): Promise { + const nativeSdk = new NativeStakingSDK() + return await nativeSdk.initPrepareForRevoke(userPublicKey, amountToUnstake) +} + +/** + * Invokes the bot to rebalance the user's funds. This is particularly useful immediately following a deposit into Marinade Native, although it is not mandatory. + * + * @param {PublicKey} userPublicKey - The PublicKey associated with the user. + */ +export async function callRebalanceHint( + userPublicKey: PublicKey +): Promise { + const nativeSdk = new NativeStakingSDK() + return await nativeSdk.callRebalanceHint(userPublicKey) +} 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": ".",