diff --git a/lerna.json b/lerna.json index d51dd73..8befc87 100644 --- a/lerna.json +++ b/lerna.json @@ -2,6 +2,6 @@ "packages": [ "packages/*" ], - "version": "7.0.3", + "version": "7.1.0-alpha.1", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } diff --git a/packages/common/package.json b/packages/common/package.json index 4ed62ae..50b6f8d 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -1,6 +1,6 @@ { "name": "@streamflow/common", - "version": "7.0.3", + "version": "7.1.0-alpha.1", "description": "Common utilities and types used by streamflow packages.", "homepage": "https://github.com/streamflow-finance/js-sdk/", "main": "./dist/esm/index.js", diff --git a/packages/distributor/package.json b/packages/distributor/package.json index 9e27026..c64ba35 100644 --- a/packages/distributor/package.json +++ b/packages/distributor/package.json @@ -1,6 +1,6 @@ { "name": "@streamflow/distributor", - "version": "7.0.3", + "version": "7.1.0-alpha.1", "description": "JavaScript SDK to interact with Streamflow Airdrop protocol.", "homepage": "https://github.com/streamflow-finance/js-sdk/", "main": "dist/esm/index.js", @@ -34,7 +34,7 @@ "typescript": "^5.6.3" }, "dependencies": { - "@coral-xyz/anchor": "^0.30.0", + "@coral-xyz/anchor": "^0.30.1", "@coral-xyz/borsh": "0.30.1", "@solana/buffer-layout": "4.0.1", "@solana/spl-token": "0.4.9", diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 1436c4f..219677f 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@streamflow/eslint-config", - "version": "7.0.3", + "version": "7.1.0-alpha.1", "license": "ISC", "main": "index.js", "files": [ diff --git a/packages/launchpad/.eslintrc b/packages/launchpad/.eslintrc new file mode 100644 index 0000000..3da2ac1 --- /dev/null +++ b/packages/launchpad/.eslintrc @@ -0,0 +1,5 @@ +{ + "extends": [ + "@streamflow/eslint-config" + ] +} diff --git a/packages/launchpad/.npmignore b/packages/launchpad/.npmignore new file mode 100644 index 0000000..011adea --- /dev/null +++ b/packages/launchpad/.npmignore @@ -0,0 +1,4 @@ +* +!dist/**/* +!package.json +!README.md diff --git a/packages/launchpad/.prettierignore b/packages/launchpad/.prettierignore new file mode 100644 index 0000000..5498e0f --- /dev/null +++ b/packages/launchpad/.prettierignore @@ -0,0 +1,2 @@ +build +coverage diff --git a/packages/launchpad/README.md b/packages/launchpad/README.md new file mode 100644 index 0000000..6c0e350 --- /dev/null +++ b/packages/launchpad/README.md @@ -0,0 +1,175 @@ +# Streamflow Launchpad + +## JS SDk to interact with the Launchpad Protocol + +This package exposes several instruction needed by both Launchpad creator and depositors: +- `create new Launchpad`; +- `fund launchpad`; +- `deposit into launchpad`; +- `claim deposited tokens`; +- `claim allocated tokens`; + +Each Launchpad consist of multiple configuration options: +- **Base Mint** - mint that users will be buying - Launchpad should be funded with enough tokens prior to users claiming their allocations; +- **Quote Mint** - mint that users will deposit and in which price of the **Base Mint** will be denominated - Launchpad authority will be able to claim deposited tokens after depositting period ends; +- **Price** - price of the 1 **Base Mint** in **Quote Mint** tokens; +- **Periods** - there are Depositing period and Vesting period needed to be configured; +- **Vesting Configuration** - dynamic vesting will be used, so Launchpad creator should provide various configuration options for it; + +## API Reference + +API Documentation available here: [docs site →](https://streamflow-finance.github.io/js-sdk/) + +## Installation + +Install the sdk with npm + +```npm + npm install @streamflow/launchpad +``` +```yarn + yarn install @streamflow/launchpad +``` +```pnpm + pnpm add @streamflow/launchpad +``` + +## Usage/Examples + +### Initiate the client +```typescript +const client = new SolanaLaunchpadClient({ + clusterUrl: "https://api.mainnet-beta.solana.com", + cluster: ICluster.Mainnet +}); +``` + +> [!WARNING] +> All operations expect ATAs to be created at the moment of execution and don't add these instructions. +> - Claim Deposits - Receiver token account that will be used to claim deposited tokens; +> - Claim Allocation - Token account for the `Base Mint` should be created prior to Depositor claiming their allocation; + + +### Read operations + +```typescript + +await client.searchLaunchpad({ baseMint, quoteMint }) // both mint parameters are optional. + +await client.getLaunchpad(id) // fetch specific Launchpad by its ID. + +await client.getDepositAccount(id) // fetch specific Deposit Account by its ID. +``` + +### Create new Launchpad +import { getBN } from "@streamflow/common"; + +```typescript +const { txId: createSig, metadataId } = await client.createLaunchpad({ + baseMint, // Mint that users will be buying + quoteMint, // Mint that users will deposit + receiver, // [optional] Token account that should receive deposits once deposit period is ended + priceOracle, // [optional] Price Oracle address that will be used in dynamic vesting + nonce: 1, // Nonce value, Launchpad PDA will be derived from nonce + baseMint + price: getBN(0.15, QUOTE_MINT_DECIMALS), // Price per 1 `baseMint` whole token denominated in `quoteMint` tokens + individualDepositingCap: getBN(1_000, QUOTE_MINT_DECIMALS), // Max Cap per User of `quoteMint` tokens to deposit + maxDepositingCap: getBN(1_000_000, QUOTE_MINT_DECIMALS), // Max global Cap of `quoteMint` tokens to deposit + depositingStartTs, // Timestamp when depositing should start + depositingEndTs, // Timestamp when depositing should end + vestingStartTs, // Timestamp when vesting should start + vestingEndTs, // Timestamp when vesting should end initially + vestingPeriod, // Period in seconds of vesting and dynamic vesting unlock updates, should be at least 30 + oracleType: "test", // [optional] Type the Price Oracle, should be aligned with the provided `priceOracle` + minPrice: 0.05, // Min Price for dynamic vesting + maxPrice: 1, // Max Price for dynamic vesting + minPercentage: 1, // Min Percentage for dynamic vesting - will be used if current price <= `minPrice` + maxPercentage: 1000, // Max Percentage for dynamic vesting - will be used if current price >= `maxPrice` + tickSize: 1, // Tick size for percentages in dynamic vesting - will be used in case minPrice < current price < maxPrice + skipInitial: false, // Whether to skip initial unlock amount update when dynamic vesting is initiated + isMemoRequired: false, // Whether to require special Memo instruction on deposit + tokenProgramId: TOKEN_PROGRAM_ID // [optional] SPL Token Program to use + }, + { invoker: authority } +); +``` + +### Deposit tokens + +```typescript +const { txId: depositSig } = await client.deposit( + { + launchpad: metadataId, // Id of the Launchpad to deposit tokens into + quoteMint, // [optional] Mint Id, if not provided it will be fetched from the Laucnhpad + amount: getBN(100.15, QUOTE_MINT_DECIMALS), // Amount of `quoteMint` tokens to deposit, + autoCap: true, // [opional] Whether to automatically cap deposited tokens in case user deposited more than `maxDepositingCap` + memo: "I don't reside in a sanctioned country.", // [optional] Text for memo instruction + owner: user1.publicKey, // [optional] Deposit owner in case it differs from the invoker + tokenProgramId: TOKEN_PROGRAM_ID // [optional] SPL Token Program to use + }, + { invoker: user1 } +); +``` + +User can deposit tokens however many times they want if both individual and max Depositing caps are respected. + +> [!Warning] +> It's not possible to withdraw deposited tokens currently. + +### Fund Launchpad + +```typescript +const { txId: fundLaunchpadSig } = await client.fundLaunchpad( + { + launchpad: metadataId, // Id of the Launchpad to fund + baseMint, // [optional] Mint Id, if not provided it will be fetched from the Laucnhpad + amount: getBN(6_666_666, BASE_MINT_DECIMALS), // Amount of base tokens to funds Laucnhpad by + tokenProgramId: TOKEN_PROGRAM_ID // [optional] SPL Token Program to use + }, + { invoker: authority } +); +``` + +> [!Note] +> Client just uses spl transfer instruction, so technically anyone can fund a Launchpad. + +### Claim deposited tokens + +```typescript +const { txId: claimDepositsSig } = await client.claimDeposits( + { + launchpad: metadataId, // Id of the Launchpad to claim deposited tokens from + quoteMint, // [optional] Mint Id, if not provided it will be fetched from the Laucnhpad + tokenProgramId: TOKEN_PROGRAM_ID // [optional] SPL Token Program to use + }, + { invoker: authority } +); +``` + +> [!Warning] +> Claiming of Deposited tokens is possible only after Deposit period has ended. + +### Claim token allocation + +```typescript +const { txId: claimAllocatedSig } = await client.claimAllocatedVested( + { + launchpad: metadataId, // Id of the Launchpad to claimed allocation from + baseMint, // [optional] Mint Id, if not provided it will be fetched from the Laucnhpad + owner: user1.publicKey, // [optional] Deposit owner in case it differs from the invoker + tokenProgramId: TOKEN_PROGRAM_ID // [optional] SPL Token Program to use + }, + { invoker: user1 } +); +``` + +> [!Note] +> Anyone can call this action on behalf of the Deposit after Deposit period has ended. +> +> This action will essentially create a Streamflow Dynamic vesting contract where: +> - total depostted amount will be equal to the Depositor token allocation; +> - initial vesting start and end time will be set according to the vesting configuration +> - in case `skipInitial` was not disabled, vesting schedule may differ depending on current price of the token; + + +> [!Warning] +> This action should be done prior to Vesting Start Time - as dynamic vesting will be used and dynamic vesting can not have any cliff amount. diff --git a/packages/launchpad/index.ts b/packages/launchpad/index.ts new file mode 100644 index 0000000..059a6d7 --- /dev/null +++ b/packages/launchpad/index.ts @@ -0,0 +1,4 @@ +export { SolanaLaunchpadClient } from "./solana/client.js"; +export * from "./solana/types.js"; +export * from "./solana/lib/derive-accounts.js"; +export * as constants from "./solana/constants.js"; diff --git a/packages/launchpad/package.json b/packages/launchpad/package.json new file mode 100644 index 0000000..ff3ae52 --- /dev/null +++ b/packages/launchpad/package.json @@ -0,0 +1,49 @@ +{ + "name": "@streamflow/launchpad", + "version": "7.1.0-alpha.1", + "description": "JavaScript SDK to interact with Streamflow Launchpad protocol.", + "homepage": "https://github.com/streamflow-finance/js-sdk/", + "main": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js", + "types": "./dist/esm/index.d.ts" + }, + "./solana": { + "import": "./dist/esm/solana/index.js", + "require": "./dist/cjs/solana/index.js", + "types": "./dist/esm/solana/index.d.ts" + } + }, + "scripts": { + "build:cjs": "rm -rf dist/cjs; tsc -p tsconfig.cjs.json", + "build:esm": "rm -rf dist/esm; tsc -p tsconfig.esm.json", + "build": "rm -rf dist; pnpm run build:cjs && pnpm run build:esm", + "pack": "pnpm build && pnpm pack", + "lint": "eslint --fix .", + "prepublishOnly": "npm run lint && npm run build" + }, + "gitHead": "a37306eba0e762af096db642fa22f07194014cfd", + "devDependencies": { + "@streamflow/eslint-config": "workspace:*", + "@types/bn.js": "5.1.1", + "typescript": "^5.6.3" + }, + "dependencies": { + "@coral-xyz/anchor": "^0.30.1", + "@coral-xyz/borsh": "^0.30.1", + "@solana/buffer-layout": "4.0.1", + "@solana/spl-token": "0.4.9", + "@solana/wallet-adapter-base": "0.9.19", + "@solana/web3.js": "1.95.4", + "@streamflow/common": "workspace:*", + "@streamflow/stream": "workspace:*", + "bn.js": "5.2.1", + "borsh": "^2.0.0", + "bs58": "5.0.0", + "p-queue": "^8.0.1" + } +} diff --git a/packages/launchpad/solana/client.ts b/packages/launchpad/solana/client.ts new file mode 100644 index 0000000..a757e64 --- /dev/null +++ b/packages/launchpad/solana/client.ts @@ -0,0 +1,464 @@ +import { + AnchorError, + Program, + ProgramAccount, + ProgramError, + parseIdlErrors, + translateError, + Address, +} from "@coral-xyz/anchor"; +import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID, createTransferCheckedInstruction } from "@solana/spl-token"; +import { + Commitment, + Connection, + ConnectionConfig, + PublicKey, + TransactionInstruction, + Keypair, + clusterApiUrl, +} from "@solana/web3.js"; +import { ContractError, ICluster, invariant, getBN, ITransactionResult } from "@streamflow/common"; +import { + buildSendThrottler, + checkOrCreateAtaBatch, + getFilters, + pk, + getMintAndProgram, + prepareTransaction, + signAndExecuteTransaction, + prepareBaseInstructions, +} from "@streamflow/common/solana"; +import { + constants as streamConstants, + OracleType, + deriveTestOraclePDA, + deriveContractPDA, + deriveEscrowPDA, +} from "@streamflow/stream/solana"; +import BN from "bn.js"; +import PQueue from "p-queue"; + +import { LAUNCHPAD_BYTE_OFFSETS, PROGRAM_ID } from "./constants.js"; +import StreamflowLaunchpadIDL from "./descriptor/idl/streamflow_launchpad.json"; +import { StreamflowLaunchpad } from "./descriptor/streamflow_launchpad.js"; +import { deriveDepositPDA, deriveLaunchpadPDA } from "./lib/derive-accounts.js"; +import { + DepositAccount, + Launchpad, + IInteractSolanaExt, + ICreateLaunchpad, + IDeposit, + IClaimDeposits, + IClaimAllocatedVested, + IFundLaunchpad, +} from "./types.js"; + +interface IInitOptions { + clusterUrl?: string; + cluster?: ICluster; + commitment?: Commitment | ConnectionConfig; + programIds?: { + launchpad?: string; + vesting?: string; + dynamicVesting?: string; + }; + sendRate?: number; + sendThrottler?: PQueue; +} + +type CreationResult = ITransactionResult & { metadataId: PublicKey }; + +export class SolanaLaunchpadClient { + connection: Connection; + + cluster: ICluster; + + private readonly commitment: Commitment | ConnectionConfig; + + private readonly sendThrottler: PQueue; + + private readonly program: Program; + + private readonly dynamicVestingId: PublicKey; + + private readonly vestingId: PublicKey; + + constructor({ + clusterUrl, + cluster = ICluster.Mainnet, + commitment = "confirmed", + programIds, + sendRate = 1, + sendThrottler, + }: IInitOptions) { + this.commitment = commitment; + if (!clusterUrl) { + switch (cluster) { + case ICluster.Mainnet: + clusterUrl = clusterApiUrl("mainnet-beta"); + break; + case ICluster.Local: + clusterUrl = "http://localhost:8899"; + break; + default: + clusterUrl = clusterApiUrl(cluster); + break; + } + } + this.connection = new Connection(clusterUrl, this.commitment); + this.cluster = cluster; + this.sendThrottler = sendThrottler ?? buildSendThrottler(sendRate); + + const launchpadIdl = { + ...StreamflowLaunchpadIDL, + address: programIds?.launchpad ?? PROGRAM_ID[cluster] ?? StreamflowLaunchpadIDL.address, + } as StreamflowLaunchpad; + this.program = new Program(launchpadIdl, { + connection: this.connection, + }) as Program; + this.dynamicVestingId = pk( + programIds?.dynamicVesting ? programIds.dynamicVesting : streamConstants.ALIGNED_UNLOCKS_PROGRAM_ID[cluster], + ); + this.vestingId = pk(programIds?.vesting ? programIds.vesting : streamConstants.PROGRAM_ID[cluster]); + } + + getCurrentProgramId(): PublicKey { + invariant(this.program, `Program is not found`); + return this.program.programId; + } + + getCommitment(): Commitment | undefined { + return typeof this.commitment == "string" ? this.commitment : this.commitment.commitment; + } + + getLaunchpad(id: Address): Promise { + return this.program.account.launchpad.fetch(id); + } + + searchLaunchpads( + criteria: Partial> = {}, + ): Promise[]> { + return this.program.account.launchpad.all(getFilters(criteria, LAUNCHPAD_BYTE_OFFSETS)); + } + + getDepositAccount(id: Address): Promise { + return this.program.account.depositAccount.fetch(id); + } + + async createLaunchpad(data: ICreateLaunchpad, extParams: IInteractSolanaExt): Promise { + const { ixs, publicKey } = await this.prepareCreateLaunchpadInstructions(data, extParams); + const { signature } = await this.execute(ixs, extParams); + return { + ixs, + txId: signature, + metadataId: publicKey, + }; + } + + async prepareCreateLaunchpadInstructions( + { + baseMint: baseMintKey, + quoteMint: quoteMintKey, + receiver, + priceOracle, + nonce, + price, + individualDepositingCap, + maxDepositingCap, + depositingStartTs, + depositingEndTs, + vestingStartTs, + vestingEndTs, + vestingPeriod, + oracleType, + minPrice, + maxPrice, + minPercentage, + maxPercentage, + tickSize, + skipInitial, + isMemoRequired, + tokenProgramId = TOKEN_PROGRAM_ID, + }: ICreateLaunchpad, + extParams: IInteractSolanaExt, + ): Promise<{ + ixs: TransactionInstruction[]; + publicKey: PublicKey; + }> { + baseMintKey = pk(baseMintKey); + quoteMintKey = pk(quoteMintKey); + const authority = extParams.invoker.publicKey; + + invariant(authority, "Undefined creator publicKey"); + + if (!priceOracle && oracleType && !["none", "test"].includes(oracleType)) { + throw new Error("Price oracle is required for the specified oracle type"); + } + + receiver = receiver ?? getAssociatedTokenAddressSync(quoteMintKey, authority); + const oracle = priceOracle ?? deriveTestOraclePDA(this.dynamicVestingId, baseMintKey, authority); + const launchpadPDA = deriveLaunchpadPDA(this.program.programId, baseMintKey, nonce); + + const createIx = await this.program.methods + .createLaunchpad({ + nonce, + price, + individualDepositingCap, + maxDepositingCap, + depositingStartTs: new BN(depositingStartTs), + depositingEndTs: new BN(depositingEndTs), + vestingStartTs: new BN(vestingStartTs), + vestingEndTs: new BN(vestingEndTs), + vestingPeriod: new BN(vestingPeriod), + oracleType: (!!oracleType ? { [oracleType]: {} } : { none: {} }) as OracleType, + minPrice: getBN(minPrice, streamConstants.ALIGNED_PRECISION_FACTOR_POW), + maxPrice: getBN(maxPrice, streamConstants.ALIGNED_PRECISION_FACTOR_POW), + minPercentage: getBN(minPercentage, streamConstants.ALIGNED_PRECISION_FACTOR_POW), + maxPercentage: getBN(maxPercentage, streamConstants.ALIGNED_PRECISION_FACTOR_POW), + tickSize: getBN(tickSize, streamConstants.ALIGNED_PRECISION_FACTOR_POW), + skipInitial, + isMemoRequired, + }) + .accounts({ + authority, + baseMint: baseMintKey, + quoteMint: quoteMintKey, + receiver: receiver, + priceOracle: oracle, + tokenProgram: tokenProgramId, + }) + .instruction(); + + return { ixs: [createIx], publicKey: launchpadPDA }; + } + + async fundLaunchpad(data: IFundLaunchpad, extParams: IInteractSolanaExt): Promise { + const { ixs } = await this.prepareFundLaunchpadInstructions(data, extParams); + const { signature } = await this.execute(ixs, extParams); + return { + ixs, + txId: signature, + }; + } + + async prepareFundLaunchpadInstructions( + { launchpad: launchpadKey, amount, baseMint: baseMintKey, tokenProgramId = TOKEN_PROGRAM_ID }: IFundLaunchpad, + extParams: IInteractSolanaExt, + ): Promise<{ + ixs: TransactionInstruction[]; + }> { + const authority = extParams.invoker.publicKey; + invariant(authority, "Undefined authority publicKey"); + + if (!baseMintKey) { + baseMintKey = (await this.getLaunchpad(launchpadKey)).baseMint; + } + const { mint } = await getMintAndProgram(this.connection, pk(baseMintKey)); + + return { + ixs: [ + ...(await checkOrCreateAtaBatch( + this.connection, + [pk(launchpadKey)], + pk(baseMintKey), + extParams.invoker, + pk(tokenProgramId), + )), + createTransferCheckedInstruction( + getAssociatedTokenAddressSync(mint.address, authority, true, pk(tokenProgramId)), + mint.address, + getAssociatedTokenAddressSync(mint.address, pk(launchpadKey), true, pk(tokenProgramId)), + authority, + BigInt(amount.toString()), + mint.decimals, + undefined, + pk(tokenProgramId), + ), + ], + }; + } + + async deposit(data: IDeposit, extParams: IInteractSolanaExt): Promise { + const { ixs } = await this.prepareDepositInstructions(data, extParams); + const { signature } = await this.execute(ixs, extParams); + return { + ixs, + txId: signature, + }; + } + + async prepareDepositInstructions( + { + launchpad: launchpadKey, + quoteMint: quoteMintKey, + amount, + autoCap = false, + memo, + owner, + tokenProgramId = TOKEN_PROGRAM_ID, + }: IDeposit, + extParams: IInteractSolanaExt, + ): Promise<{ + ixs: TransactionInstruction[]; + publicKey: PublicKey; + }> { + const payer = extParams.invoker.publicKey; + invariant(payer, "Undefined payer publicKey"); + + owner = pk(owner ?? payer); + if (!quoteMintKey) { + quoteMintKey = (await this.getLaunchpad(launchpadKey)).quoteMint; + } + + const depositPDA = deriveDepositPDA(this.program.programId, pk(launchpadKey), owner); + const ixs: TransactionInstruction[] = []; + if (memo) { + ixs.push( + new TransactionInstruction({ + keys: [{ pubkey: payer, isSigner: true, isWritable: true }], + data: Buffer.from(memo, "utf-8"), + programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"), + }), + ); + } + ixs.push( + await this.program.methods + .deposit({ amount, autoCap }) + .accounts({ + payer, + owner: owner, + from: getAssociatedTokenAddressSync(pk(quoteMintKey), payer, true, pk(tokenProgramId)), + launchpad: launchpadKey, + tokenProgram: tokenProgramId, + }) + .instruction(), + ); + + return { ixs, publicKey: depositPDA }; + } + + async claimDeposits(data: IClaimDeposits, extParams: IInteractSolanaExt): Promise { + const { ixs } = await this.prepareClaimDepositsInstructions(data, extParams); + const { signature } = await this.execute(ixs, extParams); + return { + ixs, + txId: signature, + }; + } + + async prepareClaimDepositsInstructions( + { launchpad: launchpadKey, tokenProgramId = TOKEN_PROGRAM_ID }: IClaimDeposits, + extParams: IInteractSolanaExt, + ): Promise<{ + ixs: TransactionInstruction[]; + }> { + const authority = extParams.invoker.publicKey; + invariant(authority, "Undefined authority publicKey"); + + return { + ixs: [ + await this.program.methods + .claimDeposits() + .accounts({ + authority, + launchpad: launchpadKey, + tokenProgram: tokenProgramId, + }) + .instruction(), + ], + }; + } + + async claimAllocatedVested(data: IClaimAllocatedVested, extParams: IInteractSolanaExt): Promise { + if (!extParams.computeLimit) { + extParams.computeLimit = 280_000; + } + const { ixs, streamKeypair } = await this.prepareClaimAllocatedVestedInstructions(data, extParams); + const { signature } = await this.execute(ixs, extParams, streamKeypair); + return { + ixs, + txId: signature, + }; + } + + async prepareClaimAllocatedVestedInstructions( + { launchpad: launchpadKey, baseMint: baseMintKey, owner, tokenProgramId = TOKEN_PROGRAM_ID }: IClaimAllocatedVested, + extParams: IInteractSolanaExt, + ): Promise<{ + ixs: TransactionInstruction[]; + streamKeypair: Keypair; + }> { + const payer = extParams.invoker.publicKey; + invariant(payer, "Undefined payer publicKey"); + + owner = pk(owner ?? payer); + if (!baseMintKey) { + baseMintKey = (await this.getLaunchpad(launchpadKey)).baseMint; + } + const streamKeypair = Keypair.generate(); + const streamKey = streamKeypair.publicKey; + const proxyMetadataKey = deriveContractPDA(this.dynamicVestingId, streamKeypair.publicKey); + const proxyTokensKey = getAssociatedTokenAddressSync(pk(baseMintKey), proxyMetadataKey, true); + const escrowKey = deriveEscrowPDA(this.vestingId, streamKeypair.publicKey); + const depositKey = deriveDepositPDA(this.program.programId, pk(launchpadKey), owner); + + const ix = await this.program.methods + .claimAllocatedVested() + .accounts({ + depositAccount: depositKey, + payer, + proxyMetadata: proxyMetadataKey, + proxyTokens: proxyTokensKey, + streamMetadata: streamKey, + escrowTokens: escrowKey, + withdrawor: streamConstants.WITHDRAWOR_PUBLIC_KEY, + feeOracle: streamConstants.FEE_ORACLE_PUBLIC_KEY, + tokenProgram: tokenProgramId, + }) + .accountsPartial({ + streamflowProgram: this.vestingId, + proxyProgram: this.dynamicVestingId, + }) + .instruction(); + + return { ixs: [ix], streamKeypair }; + } + + private async execute( + ixs: TransactionInstruction[], + extParams: IInteractSolanaExt, + ...partialSigners: (Keypair | undefined)[] + ) { + ixs = [...prepareBaseInstructions(this.connection, extParams), ...ixs]; + const { tx, hash, context } = await prepareTransaction( + this.connection, + ixs, + extParams.invoker.publicKey, + this.getCommitment(), + ...partialSigners, + ); + + try { + const signature = await signAndExecuteTransaction( + this.connection, + extParams.invoker, + tx, + { + hash, + context, + commitment: this.getCommitment(), + }, + { sendThrottler: this.sendThrottler }, + ); + return { signature }; + } catch (err: any) { + if (err instanceof Error) { + const parsed: AnchorError | ProgramError | typeof err = translateError(err, parseIdlErrors(this.program.idl)); + if (parsed) { + throw new ContractError(err, parsed.name, parsed.message); + } + } + throw err; + } + } +} diff --git a/packages/launchpad/solana/constants.ts b/packages/launchpad/solana/constants.ts new file mode 100644 index 0000000..d2a3b44 --- /dev/null +++ b/packages/launchpad/solana/constants.ts @@ -0,0 +1,21 @@ +import { ICluster } from "@streamflow/common"; + +export const LAUNCHPAD_PREFIX = Buffer.from("launchpad", "utf-8"); +export const VAULT_PREFIX = Buffer.from("vault", "utf-8"); +export const DEPOSIT_PREFIX = Buffer.from("deposit", "utf-8"); + +export const ANCHOR_DISCRIMINATOR_OFFSET = 8; + +export const LAUNCHPAD_BASE_MINT_OFFSET = ANCHOR_DISCRIMINATOR_OFFSET + 8; +export const LAUNCHPAD_QUOTE_MINT_OFFSET = ANCHOR_DISCRIMINATOR_OFFSET + 32; +export const LAUNCHPAD_BYTE_OFFSETS = { + baseMint: LAUNCHPAD_BASE_MINT_OFFSET, + quoteMint: LAUNCHPAD_QUOTE_MINT_OFFSET, +} as const; + +export const PROGRAM_ID: Record = { + [ICluster.Mainnet]: "BUYfFzeTWeRW5JrPjCutbsvzjA5ERS8EnGujJjfmnJu6", + [ICluster.Devnet]: "BUYfFzeTWeRW5JrPjCutbsvzjA5ERS8EnGujJjfmnJu6", + [ICluster.Testnet]: "BUYfFzeTWeRW5JrPjCutbsvzjA5ERS8EnGujJjfmnJu6", + [ICluster.Local]: "BUYfFzeTWeRW5JrPjCutbsvzjA5ERS8EnGujJjfmnJu6", +}; diff --git a/packages/launchpad/solana/descriptor/idl/streamflow_launchpad.json b/packages/launchpad/solana/descriptor/idl/streamflow_launchpad.json new file mode 100644 index 0000000..d7c3906 --- /dev/null +++ b/packages/launchpad/solana/descriptor/idl/streamflow_launchpad.json @@ -0,0 +1,1468 @@ +{ + "address": "BUYfFzeTWeRW5JrPjCutbsvzjA5ERS8EnGujJjfmnJu6", + "metadata": { + "name": "streamflow_launchpad", + "version": "1.0.0", + "spec": "0.1.0", + "description": "A Launchpad for a pre-sale" + }, + "instructions": [ + { + "name": "claim_allocated_instant", + "docs": [ + "Claim allocated funds instantly in case vesting schedule is not set" + ], + "discriminator": [ + 49, + 89, + 180, + 221, + 1, + 139, + 121, + 192 + ], + "accounts": [ + { + "name": "launchpad", + "docs": [ + "[Launchpad]." + ], + "writable": true, + "relations": [ + "deposit_account" + ] + }, + { + "name": "deposit_account", + "docs": [ + "Reward Entry that stores metadata about claimed rewards" + ], + "writable": true + }, + { + "name": "from", + "docs": [ + "Vault that stores Base tokens" + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "launchpad" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "to", + "docs": [ + "Token Account to which tokens will be transferred" + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "owner" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "payer", + "docs": [ + "Rent payer" + ], + "writable": true, + "signer": true + }, + { + "name": "owner" + }, + { + "name": "base_mint", + "docs": [ + "Base Mint of the Launchpad" + ], + "relations": [ + "launchpad" + ] + }, + { + "name": "system_program", + "docs": [ + "The [System] program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "docs": [ + "The [Associated Token] program." + ], + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "token_program", + "docs": [ + "The [Token] program." + ] + } + ], + "args": [] + }, + { + "name": "claim_allocated_vested", + "docs": [ + "Claim allocated funds in a vested manner in case vesting schedule has been set" + ], + "discriminator": [ + 14, + 116, + 137, + 52, + 178, + 218, + 187, + 229 + ], + "accounts": [ + { + "name": "launchpad", + "docs": [ + "[Launchpad]." + ], + "writable": true, + "relations": [ + "deposit_account" + ] + }, + { + "name": "deposit_account", + "docs": [ + "Reward Entry that stores metadata about claimed rewards" + ], + "writable": true + }, + { + "name": "from", + "docs": [ + "Vault that stores Base tokens" + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "launchpad" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "to", + "docs": [ + "Token Account to which tokens will be vested" + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "owner" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "base_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "payer", + "docs": [ + "Rent payer" + ], + "writable": true, + "signer": true + }, + { + "name": "owner", + "relations": [ + "deposit_account" + ] + }, + { + "name": "base_mint", + "docs": [ + "Base Mint of the Launchpad" + ], + "relations": [ + "launchpad" + ] + }, + { + "name": "price_oracle", + "relations": [ + "launchpad" + ] + }, + { + "name": "authority", + "relations": [ + "launchpad" + ] + }, + { + "name": "proxy_metadata", + "writable": true + }, + { + "name": "proxy_tokens", + "docs": [ + "Vault that stores Base tokens" + ], + "writable": true + }, + { + "name": "stream_metadata", + "writable": true, + "signer": true + }, + { + "name": "escrow_tokens", + "writable": true + }, + { + "name": "withdrawor", + "writable": true + }, + { + "name": "proxy_program", + "address": "aSTRM2NKoKxNnkmLWk9sz3k74gKBk9t7bpPrTGxMszH" + }, + { + "name": "streamflow_program", + "address": "strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m" + }, + { + "name": "fee_oracle" + }, + { + "name": "rent", + "docs": [ + "Rent account required by streamflow protocol" + ], + "address": "SysvarRent111111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "docs": [ + "The [Associated Token] program." + ], + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "token_program", + "docs": [ + "The [Token] program." + ] + }, + { + "name": "system_program", + "docs": [ + "The [System] program." + ], + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "claim_deposits", + "docs": [ + "Claim deposited funds to pre-configured `receiver` account" + ], + "discriminator": [ + 69, + 159, + 225, + 170, + 28, + 44, + 76, + 102 + ], + "accounts": [ + { + "name": "launchpad", + "docs": [ + "[Launchpad]." + ], + "writable": true + }, + { + "name": "receiver", + "docs": [ + "Deposits receiver token account" + ], + "writable": true, + "relations": [ + "launchpad" + ] + }, + { + "name": "vault", + "docs": [ + "Launchpad Vault that stores deposited tokens" + ], + "writable": true, + "relations": [ + "launchpad" + ] + }, + { + "name": "authority", + "docs": [ + "Launchpad authority" + ], + "writable": true, + "signer": true + }, + { + "name": "quote_mint", + "docs": [ + "Quote Mint of the Launchpad" + ], + "relations": [ + "launchpad" + ] + }, + { + "name": "token_program", + "docs": [ + "The [Token] program." + ] + } + ], + "args": [] + }, + { + "name": "create_launchpad", + "docs": [ + "Create new Launchpad for `base_mint` <-> `quote_mint` pair" + ], + "discriminator": [ + 193, + 189, + 26, + 187, + 44, + 137, + 43, + 190 + ], + "accounts": [ + { + "name": "launchpad", + "docs": [ + "[Launchpad]." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 108, + 97, + 117, + 110, + 99, + 104, + 112, + 97, + 100 + ] + }, + { + "kind": "account", + "path": "base_mint" + }, + { + "kind": "arg", + "path": "ix.nonce" + } + ] + } + }, + { + "name": "vault", + "docs": [ + "Escrow Account that will store deposits" + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "launchpad" + } + ] + } + }, + { + "name": "base_mint", + "docs": [ + "Base Mint of the Launchpad" + ] + }, + { + "name": "quote_mint", + "docs": [ + "Quote Mint for deposits" + ] + }, + { + "name": "price_oracle" + }, + { + "name": "authority", + "docs": [ + "Admin wallet, responsible for creating the launchpad and paying for the transaction.", + "Also is the authority" + ], + "writable": true, + "signer": true + }, + { + "name": "receiver", + "docs": [ + "Deposits receiver token account" + ], + "writable": true + }, + { + "name": "system_program", + "docs": [ + "The [System] program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "docs": [ + "The [Token] program." + ] + } + ], + "args": [ + { + "name": "ix", + "type": { + "defined": { + "name": "CreateLaunchpadIx" + } + } + } + ] + }, + { + "name": "deposit", + "docs": [ + "Deposit funds to receive allocation" + ], + "discriminator": [ + 242, + 35, + 198, + 137, + 82, + 225, + 242, + 182 + ], + "accounts": [ + { + "name": "launchpad", + "docs": [ + "[Launchpad]." + ], + "writable": true + }, + { + "name": "deposit_account", + "docs": [ + "Reward Entry that stores metadata about claimed rewards" + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 100, + 101, + 112, + 111, + 115, + 105, + 116 + ] + }, + { + "kind": "account", + "path": "launchpad" + }, + { + "kind": "account", + "path": "owner" + } + ] + } + }, + { + "name": "from", + "docs": [ + "Token Account from which stake tokens will be transferred" + ], + "writable": true + }, + { + "name": "vault", + "docs": [ + "Stake Pool Vault that stores staked tokens" + ], + "writable": true, + "relations": [ + "launchpad" + ] + }, + { + "name": "payer", + "docs": [ + "Rent payer" + ], + "writable": true, + "signer": true + }, + { + "name": "owner" + }, + { + "name": "base_mint", + "docs": [ + "Base Mint of the Launchpad" + ], + "relations": [ + "launchpad" + ] + }, + { + "name": "quote_mint", + "docs": [ + "Quote Mint of the Launchpad" + ], + "relations": [ + "launchpad" + ] + }, + { + "name": "system_program", + "docs": [ + "The [System] program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "docs": [ + "The [Token] program." + ] + } + ], + "args": [ + { + "name": "ix", + "type": { + "defined": { + "name": "DepositIx" + } + } + } + ] + } + ], + "accounts": [ + { + "name": "DepositAccount", + "discriminator": [ + 148, + 37, + 207, + 116, + 61, + 33, + 53, + 179 + ] + }, + { + "name": "Launchpad", + "discriminator": [ + 247, + 20, + 16, + 242, + 203, + 38, + 169, + 160 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "Unauthorized", + "msg": "Authority does not have permission for this action" + }, + { + "code": 6001, + "name": "ArithmeticError", + "msg": "Arithmetic error" + }, + { + "code": 6002, + "name": "InvalidMemo", + "msg": "Provided memo is invalid or not provided" + }, + { + "code": 6003, + "name": "InvalidParams", + "msg": "Invalid Parameters provided" + }, + { + "code": 6004, + "name": "InvalidBaseMint", + "msg": "Invalid Base Mint" + }, + { + "code": 6005, + "name": "InvalidQuoteMint", + "msg": "Invalid Quote Mint" + }, + { + "code": 6006, + "name": "InvalidVault", + "msg": "Invalid Vault" + }, + { + "code": 6007, + "name": "InvalidLaunchpad", + "msg": "Invalid Launchpad" + }, + { + "code": 6008, + "name": "InvalidReceiver", + "msg": "Invalid Receiver provided" + }, + { + "code": 6009, + "name": "DepositingNotStarted", + "msg": "Depositing has not started yet" + }, + { + "code": 6010, + "name": "DepositingEnded", + "msg": "Depositing period has ended" + }, + { + "code": 6011, + "name": "LaunchpadCancelled", + "msg": "Launchpad has been cancelled" + }, + { + "code": 6012, + "name": "AmountMoreThanIndividualCap", + "msg": "Deposit amount is more than Individual Cap" + }, + { + "code": 6013, + "name": "AmountMoreThanMaxCap", + "msg": "Deposit amount is more than Max Cap" + }, + { + "code": 6014, + "name": "DepositingNotEnded", + "msg": "Depositing period has not ended yet" + }, + { + "code": 6015, + "name": "DepositsAlreadyClaimed", + "msg": "Deposits has been already claimed" + }, + { + "code": 6016, + "name": "VestingNotStarted", + "msg": "Vesting period has not started yet" + }, + { + "code": 6017, + "name": "VestingAlreadyClaimed", + "msg": "Allocated amount has been claimed already" + }, + { + "code": 6018, + "name": "InstantClaimNotAllowed", + "msg": "Instant claiming not allowed" + }, + { + "code": 6019, + "name": "VestedClaimNotAllowed", + "msg": "Vested claiming not allowed" + }, + { + "code": 6020, + "name": "VestingCantBeClaimed", + "msg": "Vested can't be claimed after it started" + } + ], + "types": [ + { + "name": "CreateLaunchpadIx", + "type": { + "kind": "struct", + "fields": [ + { + "name": "nonce", + "type": "u8" + }, + { + "name": "price", + "docs": [ + "Price in `quote_mint` decimal value for every whole `base_mint` token" + ], + "type": "u64" + }, + { + "name": "individual_depositing_cap", + "docs": [ + "Individual Depositing Cap in `quote_mint` Tokens" + ], + "type": "u64" + }, + { + "name": "max_depositing_cap", + "docs": [ + "Max Depositing Cap in `quote_mint` Tokens" + ], + "type": "u64" + }, + { + "name": "depositing_start_ts", + "docs": [ + "When depositing should start" + ], + "type": "u64" + }, + { + "name": "depositing_end_ts", + "docs": [ + "When depositing should end" + ], + "type": "u64" + }, + { + "name": "vesting_start_ts", + "docs": [ + "When vesting contract start time should be" + ], + "type": "u64" + }, + { + "name": "vesting_period", + "docs": [ + "Vesting release period in seconds" + ], + "type": "u64" + }, + { + "name": "vesting_end_ts", + "docs": [ + "When vesting contract end time should be" + ], + "type": "u64" + }, + { + "name": "min_price", + "docs": [ + "Min price for dynamic vesting" + ], + "type": "u64" + }, + { + "name": "max_price", + "docs": [ + "Max price for dynamic vesting" + ], + "type": "u64" + }, + { + "name": "min_percentage", + "docs": [ + "Min percentage for dynamic vesting" + ], + "type": "u64" + }, + { + "name": "max_percentage", + "docs": [ + "Max percentage for dynamic vesting" + ], + "type": "u64" + }, + { + "name": "tick_size", + "docs": [ + "Size of the tick in dynamic vesting" + ], + "type": "u64" + }, + { + "name": "skip_initial", + "docs": [ + "Whether to skip initial calculation of amount per period in dynamic vesting" + ], + "type": "bool" + }, + { + "name": "oracle_type", + "docs": [ + "Type of Oracle to use to derive Token Price" + ], + "type": { + "defined": { + "name": "OracleType" + } + } + }, + { + "name": "is_memo_required", + "docs": [ + "Whether to require memo on deposit" + ], + "type": "bool" + } + ] + } + }, + { + "name": "DepositAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "launchpad", + "docs": [ + "Launchpad to which deposit belongs" + ], + "type": "pubkey" + }, + { + "name": "owner", + "docs": [ + "Deposit owner" + ], + "type": "pubkey" + }, + { + "name": "amount", + "docs": [ + "Amount of funds deposited in `quote_mint`" + ], + "type": "u64" + }, + { + "name": "allocated_amount", + "docs": [ + "Amount allocated according to amount deposited" + ], + "type": "u64" + }, + { + "name": "created_ts", + "docs": [ + "Time when deposit was done initially" + ], + "type": "u64" + }, + { + "name": "claimed_ts", + "docs": [ + "Time when `allocated_amount` was claimed" + ], + "type": "u64" + }, + { + "name": "_buffer", + "docs": [ + "Buffer for additional fields" + ], + "type": { + "array": [ + "u8", + 64 + ] + } + } + ] + } + }, + { + "name": "DepositIx", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount", + "docs": [ + "Amount to deposit into the launchpad" + ], + "type": "u64" + }, + { + "name": "auto_cap", + "docs": [ + "Whether to allow automatically cap the amount in case amount exceeds `max_depositing_cap`" + ], + "type": "bool" + } + ] + } + }, + { + "name": "Launchpad", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "docs": [ + "Bump Seed used to sign transactions" + ], + "type": "u8" + }, + { + "name": "nonce", + "docs": [ + "Nonce to differentiate pools for the same mint" + ], + "type": "u8" + }, + { + "name": "_filler", + "docs": [ + "Filler to account for alignment" + ], + "type": { + "array": [ + "u8", + 6 + ] + } + }, + { + "name": "base_mint", + "docs": [ + "Mint of the Launchpad Token" + ], + "type": "pubkey" + }, + { + "name": "quote_mint", + "docs": [ + "Mint of the Token to exchange base mint to" + ], + "type": "pubkey" + }, + { + "name": "authority", + "docs": [ + "Current authority" + ], + "type": "pubkey" + }, + { + "name": "receiver", + "docs": [ + "Token account that should receive deposits after launchpad deposits end" + ], + "type": "pubkey" + }, + { + "name": "price_oracle", + "docs": [ + "Oracle account for price deriviation in vesting" + ], + "type": "pubkey" + }, + { + "name": "vault", + "docs": [ + "Escrow Account that stores deposited tokens" + ], + "type": "pubkey" + }, + { + "name": "config", + "docs": [ + "Configuration" + ], + "type": { + "defined": { + "name": "LaunchpadConfig" + } + } + }, + { + "name": "vesting_config", + "docs": [ + "Vesting Configuration" + ], + "type": { + "defined": { + "name": "VestingConfig" + } + } + }, + { + "name": "state", + "docs": [ + "Current state of the Launchpad" + ], + "type": { + "defined": { + "name": "LaunchpadState" + } + } + }, + { + "name": "_buffer", + "docs": [ + "Buffer for additional fields" + ], + "type": { + "array": [ + "u8", + 64 + ] + } + } + ] + } + }, + { + "name": "LaunchpadConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "price", + "docs": [ + "Price in `quote_mint` decimal value for every whole `base_mint` token" + ], + "type": "u64" + }, + { + "name": "individual_depositing_cap", + "docs": [ + "Individual Depositing Cap in `quote_mint` Tokens" + ], + "type": "u64" + }, + { + "name": "max_depositing_cap", + "docs": [ + "Max Depositing Cap in `quote_mint` Tokens" + ], + "type": "u64" + }, + { + "name": "depositing_start_ts", + "docs": [ + "When depositing should start" + ], + "type": "u64" + }, + { + "name": "depositing_end_ts", + "docs": [ + "When depositing should end" + ], + "type": "u64" + }, + { + "name": "is_memo_required", + "docs": [ + "Whether to require memo on deposit" + ], + "type": "bool" + } + ] + } + }, + { + "name": "LaunchpadState", + "type": { + "kind": "struct", + "fields": [ + { + "name": "total_deposited_amount", + "docs": [ + "Total amount of `quote_mint` tokens deposited" + ], + "type": "u64" + }, + { + "name": "total_deposited_users", + "docs": [ + "Total amount of users who deposited tokens" + ], + "type": "u64" + }, + { + "name": "total_claimed_users", + "docs": [ + "Number of users who have claimed `base_mint` tokens already" + ], + "type": "u64" + }, + { + "name": "created_ts", + "docs": [ + "Time when Launchpad was created" + ], + "type": "u64" + }, + { + "name": "claimed_ts", + "docs": [ + "Time when deposits where claimed to authority token account" + ], + "type": "u64" + }, + { + "name": "cancelled_ts", + "docs": [ + "Time when Launchpad was cancelled, makes withdrawals possible" + ], + "type": "u64" + }, + { + "name": "_buffer", + "docs": [ + "Buffer for additional fields" + ], + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + } + }, + { + "name": "OracleType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "None" + }, + { + "name": "Test" + }, + { + "name": "Pyth" + }, + { + "name": "Switchboard" + } + ] + } + }, + { + "name": "VestingConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "start_ts", + "docs": [ + "When vesting contract start time should be" + ], + "type": "u64" + }, + { + "name": "period", + "docs": [ + "Vesting unlock/update period in seconds" + ], + "type": "u64" + }, + { + "name": "end_ts", + "docs": [ + "When vesting contract end time should be" + ], + "type": "u64" + }, + { + "name": "min_price", + "docs": [ + "Min price boundary" + ], + "type": "u64" + }, + { + "name": "max_price", + "docs": [ + "Max price boundary" + ], + "type": "u64" + }, + { + "name": "min_percentage", + "docs": [ + "Min percentage boundary, can be 0 that equals 1 Raw Token" + ], + "type": "u64" + }, + { + "name": "max_percentage", + "docs": [ + "Max percentage boundary" + ], + "type": "u64" + }, + { + "name": "tick_size", + "docs": [ + "Ticket size for percentage boundaries" + ], + "type": "u64" + }, + { + "name": "skip_initial", + "docs": [ + "Whether to skip initial calculation of amount per period" + ], + "type": "bool" + }, + { + "name": "oracle_type", + "docs": [ + "Type of Oracle to use to derive Token Price" + ], + "type": { + "defined": { + "name": "OracleType" + } + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/packages/launchpad/solana/descriptor/streamflow_launchpad.ts b/packages/launchpad/solana/descriptor/streamflow_launchpad.ts new file mode 100644 index 0000000..ed1cc3d --- /dev/null +++ b/packages/launchpad/solana/descriptor/streamflow_launchpad.ts @@ -0,0 +1,1133 @@ +/** + * Program IDL in camelCase format in order to be used in JS/TS. + * + * Note that this is only a type helper and is not the actual IDL. The original + * IDL can be found at `target/idl/streamflow_launchpad.json`. + */ +export type StreamflowLaunchpad = { + address: "BUYfFzeTWeRW5JrPjCutbsvzjA5ERS8EnGujJjfmnJu6"; + metadata: { + name: "streamflowLaunchpad"; + version: "1.0.0"; + spec: "0.1.0"; + description: "A Launchpad for a pre-sale"; + }; + instructions: [ + { + name: "claimAllocatedInstant"; + docs: ["Claim allocated funds instantly in case vesting schedule is not set"]; + discriminator: [49, 89, 180, 221, 1, 139, 121, 192]; + accounts: [ + { + name: "launchpad"; + docs: ["[Launchpad]."]; + writable: true; + relations: ["depositAccount"]; + }, + { + name: "depositAccount"; + docs: ["Reward Entry that stores metadata about claimed rewards"]; + writable: true; + }, + { + name: "from"; + docs: ["Vault that stores Base tokens"]; + writable: true; + pda: { + seeds: [ + { + kind: "account"; + path: "launchpad"; + }, + { + kind: "account"; + path: "tokenProgram"; + }, + { + kind: "account"; + path: "baseMint"; + }, + ]; + program: { + kind: "const"; + value: [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89, + ]; + }; + }; + }, + { + name: "to"; + docs: ["Token Account to which tokens will be transferred"]; + writable: true; + pda: { + seeds: [ + { + kind: "account"; + path: "owner"; + }, + { + kind: "account"; + path: "tokenProgram"; + }, + { + kind: "account"; + path: "baseMint"; + }, + ]; + program: { + kind: "const"; + value: [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89, + ]; + }; + }; + }, + { + name: "payer"; + docs: ["Rent payer"]; + writable: true; + signer: true; + }, + { + name: "owner"; + }, + { + name: "baseMint"; + docs: ["Base Mint of the Launchpad"]; + relations: ["launchpad"]; + }, + { + name: "systemProgram"; + docs: ["The [System] program."]; + address: "11111111111111111111111111111111"; + }, + { + name: "associatedTokenProgram"; + docs: ["The [Associated Token] program."]; + address: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"; + }, + { + name: "tokenProgram"; + docs: ["The [Token] program."]; + }, + ]; + args: []; + }, + { + name: "claimAllocatedVested"; + docs: ["Claim allocated funds in a vested manner in case vesting schedule has been set"]; + discriminator: [14, 116, 137, 52, 178, 218, 187, 229]; + accounts: [ + { + name: "launchpad"; + docs: ["[Launchpad]."]; + writable: true; + relations: ["depositAccount"]; + }, + { + name: "depositAccount"; + docs: ["Reward Entry that stores metadata about claimed rewards"]; + writable: true; + }, + { + name: "from"; + docs: ["Vault that stores Base tokens"]; + writable: true; + pda: { + seeds: [ + { + kind: "account"; + path: "launchpad"; + }, + { + kind: "account"; + path: "tokenProgram"; + }, + { + kind: "account"; + path: "baseMint"; + }, + ]; + program: { + kind: "const"; + value: [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89, + ]; + }; + }; + }, + { + name: "to"; + docs: ["Token Account to which tokens will be vested"]; + writable: true; + pda: { + seeds: [ + { + kind: "account"; + path: "owner"; + }, + { + kind: "account"; + path: "tokenProgram"; + }, + { + kind: "account"; + path: "baseMint"; + }, + ]; + program: { + kind: "const"; + value: [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89, + ]; + }; + }; + }, + { + name: "payer"; + docs: ["Rent payer"]; + writable: true; + signer: true; + }, + { + name: "owner"; + relations: ["depositAccount"]; + }, + { + name: "baseMint"; + docs: ["Base Mint of the Launchpad"]; + relations: ["launchpad"]; + }, + { + name: "priceOracle"; + relations: ["launchpad"]; + }, + { + name: "authority"; + relations: ["launchpad"]; + }, + { + name: "proxyMetadata"; + writable: true; + }, + { + name: "proxyTokens"; + docs: ["Vault that stores Base tokens"]; + writable: true; + }, + { + name: "streamMetadata"; + writable: true; + signer: true; + }, + { + name: "escrowTokens"; + writable: true; + }, + { + name: "withdrawor"; + writable: true; + }, + { + name: "proxyProgram"; + address: "aSTRM2NKoKxNnkmLWk9sz3k74gKBk9t7bpPrTGxMszH"; + }, + { + name: "streamflowProgram"; + address: "strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m"; + }, + { + name: "feeOracle"; + }, + { + name: "rent"; + docs: ["Rent account required by streamflow protocol"]; + address: "SysvarRent111111111111111111111111111111111"; + }, + { + name: "associatedTokenProgram"; + docs: ["The [Associated Token] program."]; + address: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"; + }, + { + name: "tokenProgram"; + docs: ["The [Token] program."]; + }, + { + name: "systemProgram"; + docs: ["The [System] program."]; + address: "11111111111111111111111111111111"; + }, + ]; + args: []; + }, + { + name: "claimDeposits"; + docs: ["Claim deposited funds to pre-configured `receiver` account"]; + discriminator: [69, 159, 225, 170, 28, 44, 76, 102]; + accounts: [ + { + name: "launchpad"; + docs: ["[Launchpad]."]; + writable: true; + }, + { + name: "receiver"; + docs: ["Deposits receiver token account"]; + writable: true; + relations: ["launchpad"]; + }, + { + name: "vault"; + docs: ["Launchpad Vault that stores deposited tokens"]; + writable: true; + relations: ["launchpad"]; + }, + { + name: "authority"; + docs: ["Launchpad authority"]; + writable: true; + signer: true; + }, + { + name: "quoteMint"; + docs: ["Quote Mint of the Launchpad"]; + relations: ["launchpad"]; + }, + { + name: "tokenProgram"; + docs: ["The [Token] program."]; + }, + ]; + args: []; + }, + { + name: "createLaunchpad"; + docs: ["Create new Launchpad for `base_mint` <-> `quote_mint` pair"]; + discriminator: [193, 189, 26, 187, 44, 137, 43, 190]; + accounts: [ + { + name: "launchpad"; + docs: ["[Launchpad]."]; + writable: true; + pda: { + seeds: [ + { + kind: "const"; + value: [108, 97, 117, 110, 99, 104, 112, 97, 100]; + }, + { + kind: "account"; + path: "baseMint"; + }, + { + kind: "arg"; + path: "ix.nonce"; + }, + ]; + }; + }, + { + name: "vault"; + docs: ["Escrow Account that will store deposits"]; + writable: true; + pda: { + seeds: [ + { + kind: "const"; + value: [118, 97, 117, 108, 116]; + }, + { + kind: "account"; + path: "launchpad"; + }, + ]; + }; + }, + { + name: "baseMint"; + docs: ["Base Mint of the Launchpad"]; + }, + { + name: "quoteMint"; + docs: ["Quote Mint for deposits"]; + }, + { + name: "priceOracle"; + }, + { + name: "authority"; + docs: [ + "Admin wallet, responsible for creating the launchpad and paying for the transaction.", + "Also is the authority", + ]; + writable: true; + signer: true; + }, + { + name: "receiver"; + docs: ["Deposits receiver token account"]; + writable: true; + }, + { + name: "systemProgram"; + docs: ["The [System] program."]; + address: "11111111111111111111111111111111"; + }, + { + name: "tokenProgram"; + docs: ["The [Token] program."]; + }, + ]; + args: [ + { + name: "ix"; + type: { + defined: { + name: "createLaunchpadIx"; + }; + }; + }, + ]; + }, + { + name: "deposit"; + docs: ["Deposit funds to receive allocation"]; + discriminator: [242, 35, 198, 137, 82, 225, 242, 182]; + accounts: [ + { + name: "launchpad"; + docs: ["[Launchpad]."]; + writable: true; + }, + { + name: "depositAccount"; + docs: ["Reward Entry that stores metadata about claimed rewards"]; + writable: true; + pda: { + seeds: [ + { + kind: "const"; + value: [100, 101, 112, 111, 115, 105, 116]; + }, + { + kind: "account"; + path: "launchpad"; + }, + { + kind: "account"; + path: "owner"; + }, + ]; + }; + }, + { + name: "from"; + docs: ["Token Account from which stake tokens will be transferred"]; + writable: true; + }, + { + name: "vault"; + docs: ["Stake Pool Vault that stores staked tokens"]; + writable: true; + relations: ["launchpad"]; + }, + { + name: "payer"; + docs: ["Rent payer"]; + writable: true; + signer: true; + }, + { + name: "owner"; + }, + { + name: "baseMint"; + docs: ["Base Mint of the Launchpad"]; + relations: ["launchpad"]; + }, + { + name: "quoteMint"; + docs: ["Quote Mint of the Launchpad"]; + relations: ["launchpad"]; + }, + { + name: "systemProgram"; + docs: ["The [System] program."]; + address: "11111111111111111111111111111111"; + }, + { + name: "tokenProgram"; + docs: ["The [Token] program."]; + }, + ]; + args: [ + { + name: "ix"; + type: { + defined: { + name: "depositIx"; + }; + }; + }, + ]; + }, + ]; + accounts: [ + { + name: "depositAccount"; + discriminator: [148, 37, 207, 116, 61, 33, 53, 179]; + }, + { + name: "launchpad"; + discriminator: [247, 20, 16, 242, 203, 38, 169, 160]; + }, + ]; + errors: [ + { + code: 6000; + name: "unauthorized"; + msg: "Authority does not have permission for this action"; + }, + { + code: 6001; + name: "arithmeticError"; + msg: "Arithmetic error"; + }, + { + code: 6002; + name: "invalidMemo"; + msg: "Provided memo is invalid or not provided"; + }, + { + code: 6003; + name: "invalidParams"; + msg: "Invalid Parameters provided"; + }, + { + code: 6004; + name: "invalidBaseMint"; + msg: "Invalid Base Mint"; + }, + { + code: 6005; + name: "invalidQuoteMint"; + msg: "Invalid Quote Mint"; + }, + { + code: 6006; + name: "invalidVault"; + msg: "Invalid Vault"; + }, + { + code: 6007; + name: "invalidLaunchpad"; + msg: "Invalid Launchpad"; + }, + { + code: 6008; + name: "invalidReceiver"; + msg: "Invalid Receiver provided"; + }, + { + code: 6009; + name: "depositingNotStarted"; + msg: "Depositing has not started yet"; + }, + { + code: 6010; + name: "depositingEnded"; + msg: "Depositing period has ended"; + }, + { + code: 6011; + name: "launchpadCancelled"; + msg: "Launchpad has been cancelled"; + }, + { + code: 6012; + name: "amountMoreThanIndividualCap"; + msg: "Deposit amount is more than Individual Cap"; + }, + { + code: 6013; + name: "amountMoreThanMaxCap"; + msg: "Deposit amount is more than Max Cap"; + }, + { + code: 6014; + name: "depositingNotEnded"; + msg: "Depositing period has not ended yet"; + }, + { + code: 6015; + name: "depositsAlreadyClaimed"; + msg: "Deposits has been already claimed"; + }, + { + code: 6016; + name: "vestingNotStarted"; + msg: "Vesting period has not started yet"; + }, + { + code: 6017; + name: "vestingAlreadyClaimed"; + msg: "Allocated amount has been claimed already"; + }, + { + code: 6018; + name: "instantClaimNotAllowed"; + msg: "Instant claiming not allowed"; + }, + { + code: 6019; + name: "vestedClaimNotAllowed"; + msg: "Vested claiming not allowed"; + }, + { + code: 6020; + name: "vestingCantBeClaimed"; + msg: "Vested can't be claimed after it started"; + }, + ]; + types: [ + { + name: "createLaunchpadIx"; + type: { + kind: "struct"; + fields: [ + { + name: "nonce"; + type: "u8"; + }, + { + name: "price"; + docs: ["Price in `quote_mint` decimal value for every whole `base_mint` token"]; + type: "u64"; + }, + { + name: "individualDepositingCap"; + docs: ["Individual Depositing Cap in `quote_mint` Tokens"]; + type: "u64"; + }, + { + name: "maxDepositingCap"; + docs: ["Max Depositing Cap in `quote_mint` Tokens"]; + type: "u64"; + }, + { + name: "depositingStartTs"; + docs: ["When depositing should start"]; + type: "u64"; + }, + { + name: "depositingEndTs"; + docs: ["When depositing should end"]; + type: "u64"; + }, + { + name: "vestingStartTs"; + docs: ["When vesting contract start time should be"]; + type: "u64"; + }, + { + name: "vestingPeriod"; + docs: ["Vesting release period in seconds"]; + type: "u64"; + }, + { + name: "vestingEndTs"; + docs: ["When vesting contract end time should be"]; + type: "u64"; + }, + { + name: "minPrice"; + docs: ["Min price for dynamic vesting"]; + type: "u64"; + }, + { + name: "maxPrice"; + docs: ["Max price for dynamic vesting"]; + type: "u64"; + }, + { + name: "minPercentage"; + docs: ["Min percentage for dynamic vesting"]; + type: "u64"; + }, + { + name: "maxPercentage"; + docs: ["Max percentage for dynamic vesting"]; + type: "u64"; + }, + { + name: "tickSize"; + docs: ["Size of the tick in dynamic vesting"]; + type: "u64"; + }, + { + name: "skipInitial"; + docs: ["Whether to skip initial calculation of amount per period in dynamic vesting"]; + type: "bool"; + }, + { + name: "oracleType"; + docs: ["Type of Oracle to use to derive Token Price"]; + type: { + defined: { + name: "oracleType"; + }; + }; + }, + { + name: "isMemoRequired"; + docs: ["Whether to require memo on deposit"]; + type: "bool"; + }, + ]; + }; + }, + { + name: "depositAccount"; + type: { + kind: "struct"; + fields: [ + { + name: "launchpad"; + docs: ["Launchpad to which deposit belongs"]; + type: "pubkey"; + }, + { + name: "owner"; + docs: ["Deposit owner"]; + type: "pubkey"; + }, + { + name: "amount"; + docs: ["Amount of funds deposited in `quote_mint`"]; + type: "u64"; + }, + { + name: "allocatedAmount"; + docs: ["Amount allocated according to amount deposited"]; + type: "u64"; + }, + { + name: "createdTs"; + docs: ["Time when deposit was done initially"]; + type: "u64"; + }, + { + name: "claimedTs"; + docs: ["Time when `allocated_amount` was claimed"]; + type: "u64"; + }, + { + name: "buffer"; + docs: ["Buffer for additional fields"]; + type: { + array: ["u8", 64]; + }; + }, + ]; + }; + }, + { + name: "depositIx"; + type: { + kind: "struct"; + fields: [ + { + name: "amount"; + docs: ["Amount to deposit into the launchpad"]; + type: "u64"; + }, + { + name: "autoCap"; + docs: ["Whether to allow automatically cap the amount in case amount exceeds `max_depositing_cap`"]; + type: "bool"; + }, + ]; + }; + }, + { + name: "launchpad"; + type: { + kind: "struct"; + fields: [ + { + name: "bump"; + docs: ["Bump Seed used to sign transactions"]; + type: "u8"; + }, + { + name: "nonce"; + docs: ["Nonce to differentiate pools for the same mint"]; + type: "u8"; + }, + { + name: "filler"; + docs: ["Filler to account for alignment"]; + type: { + array: ["u8", 6]; + }; + }, + { + name: "baseMint"; + docs: ["Mint of the Launchpad Token"]; + type: "pubkey"; + }, + { + name: "quoteMint"; + docs: ["Mint of the Token to exchange base mint to"]; + type: "pubkey"; + }, + { + name: "authority"; + docs: ["Current authority"]; + type: "pubkey"; + }, + { + name: "receiver"; + docs: ["Token account that should receive deposits after launchpad deposits end"]; + type: "pubkey"; + }, + { + name: "priceOracle"; + docs: ["Oracle account for price deriviation in vesting"]; + type: "pubkey"; + }, + { + name: "vault"; + docs: ["Escrow Account that stores deposited tokens"]; + type: "pubkey"; + }, + { + name: "config"; + docs: ["Configuration"]; + type: { + defined: { + name: "launchpadConfig"; + }; + }; + }, + { + name: "vestingConfig"; + docs: ["Vesting Configuration"]; + type: { + defined: { + name: "vestingConfig"; + }; + }; + }, + { + name: "state"; + docs: ["Current state of the Launchpad"]; + type: { + defined: { + name: "launchpadState"; + }; + }; + }, + { + name: "buffer"; + docs: ["Buffer for additional fields"]; + type: { + array: ["u8", 64]; + }; + }, + ]; + }; + }, + { + name: "launchpadConfig"; + type: { + kind: "struct"; + fields: [ + { + name: "price"; + docs: ["Price in `quote_mint` decimal value for every whole `base_mint` token"]; + type: "u64"; + }, + { + name: "individualDepositingCap"; + docs: ["Individual Depositing Cap in `quote_mint` Tokens"]; + type: "u64"; + }, + { + name: "maxDepositingCap"; + docs: ["Max Depositing Cap in `quote_mint` Tokens"]; + type: "u64"; + }, + { + name: "depositingStartTs"; + docs: ["When depositing should start"]; + type: "u64"; + }, + { + name: "depositingEndTs"; + docs: ["When depositing should end"]; + type: "u64"; + }, + { + name: "isMemoRequired"; + docs: ["Whether to require memo on deposit"]; + type: "bool"; + }, + ]; + }; + }, + { + name: "launchpadState"; + type: { + kind: "struct"; + fields: [ + { + name: "totalDepositedAmount"; + docs: ["Total amount of `quote_mint` tokens deposited"]; + type: "u64"; + }, + { + name: "totalDepositedUsers"; + docs: ["Total amount of users who deposited tokens"]; + type: "u64"; + }, + { + name: "totalClaimedUsers"; + docs: ["Number of users who have claimed `base_mint` tokens already"]; + type: "u64"; + }, + { + name: "createdTs"; + docs: ["Time when Launchpad was created"]; + type: "u64"; + }, + { + name: "claimedTs"; + docs: ["Time when deposits where claimed to authority token account"]; + type: "u64"; + }, + { + name: "cancelledTs"; + docs: ["Time when Launchpad was cancelled, makes withdrawals possible"]; + type: "u64"; + }, + { + name: "buffer"; + docs: ["Buffer for additional fields"]; + type: { + array: ["u8", 32]; + }; + }, + ]; + }; + }, + { + name: "oracleType"; + type: { + kind: "enum"; + variants: [ + { + name: "none"; + }, + { + name: "test"; + }, + { + name: "pyth"; + }, + { + name: "switchboard"; + }, + ]; + }; + }, + { + name: "vestingConfig"; + type: { + kind: "struct"; + fields: [ + { + name: "startTs"; + docs: ["When vesting contract start time should be"]; + type: "u64"; + }, + { + name: "period"; + docs: ["Vesting unlock/update period in seconds"]; + type: "u64"; + }, + { + name: "endTs"; + docs: ["When vesting contract end time should be"]; + type: "u64"; + }, + { + name: "minPrice"; + docs: ["Min price boundary"]; + type: "u64"; + }, + { + name: "maxPrice"; + docs: ["Max price boundary"]; + type: "u64"; + }, + { + name: "minPercentage"; + docs: ["Min percentage boundary, can be 0 that equals 1 Raw Token"]; + type: "u64"; + }, + { + name: "maxPercentage"; + docs: ["Max percentage boundary"]; + type: "u64"; + }, + { + name: "tickSize"; + docs: ["Ticket size for percentage boundaries"]; + type: "u64"; + }, + { + name: "skipInitial"; + docs: ["Whether to skip initial calculation of amount per period"]; + type: "bool"; + }, + { + name: "oracleType"; + docs: ["Type of Oracle to use to derive Token Price"]; + type: { + defined: { + name: "oracleType"; + }; + }; + }, + ]; + }; + }, + ]; +}; diff --git a/packages/launchpad/solana/lib/derive-accounts.ts b/packages/launchpad/solana/lib/derive-accounts.ts new file mode 100644 index 0000000..701c75c --- /dev/null +++ b/packages/launchpad/solana/lib/derive-accounts.ts @@ -0,0 +1,20 @@ +import { PublicKey } from "@solana/web3.js"; +// eslint-disable-next-line no-restricted-imports +import BN from "bn.js"; + +import { LAUNCHPAD_PREFIX, DEPOSIT_PREFIX, VAULT_PREFIX } from "../constants.js"; + +export const deriveLaunchpadPDA = (programId: PublicKey, baseMint: PublicKey, nonce: number): PublicKey => { + return PublicKey.findProgramAddressSync( + [LAUNCHPAD_PREFIX, baseMint.toBuffer(), new BN(nonce).toArrayLike(Buffer, "le", 1)], + programId, + )[0]; +}; + +export const deriveDepositPDA = (programId: PublicKey, launchpad: PublicKey, owner: PublicKey): PublicKey => { + return PublicKey.findProgramAddressSync([DEPOSIT_PREFIX, launchpad.toBuffer(), owner.toBuffer()], programId)[0]; +}; + +export const deriveVaultPDA = (programId: PublicKey, launchpad: PublicKey): PublicKey => { + return PublicKey.findProgramAddressSync([VAULT_PREFIX, launchpad.toBuffer()], programId)[0]; +}; diff --git a/packages/launchpad/solana/types.ts b/packages/launchpad/solana/types.ts new file mode 100644 index 0000000..d3ebefe --- /dev/null +++ b/packages/launchpad/solana/types.ts @@ -0,0 +1,70 @@ +import { Address, type IdlTypes } from "@coral-xyz/anchor"; +import { SignerWalletAdapter } from "@solana/wallet-adapter-base"; +import { Keypair } from "@solana/web3.js"; +import { ITransactionSolanaExt } from "@streamflow/common/solana"; +import { OracleTypeName } from "@streamflow/stream"; +import BN from "bn.js"; + +import { StreamflowLaunchpad } from "./descriptor/streamflow_launchpad.js"; + +export type Launchpad = IdlTypes["launchpad"]; +export type DepositAccount = IdlTypes["depositAccount"]; + +export interface IInteractSolanaExt extends ITransactionSolanaExt { + invoker: SignerWalletAdapter | Keypair; +} + +interface ILaunchpad { + launchpad: Address; + baseMint?: Address; + quoteMint?: Address; +} + +interface IOwner { + owner?: Address; +} + +interface ITokenProgram { + tokenProgramId?: Address; +} + +export interface ICreateLaunchpad extends ITokenProgram { + baseMint: Address; + quoteMint: Address; + receiver?: Address; + priceOracle?: Address; + + nonce: number; + price: BN; + individualDepositingCap: BN; + maxDepositingCap: BN; + depositingStartTs: number | BN; + depositingEndTs: number | BN; + vestingStartTs: number | BN; + vestingEndTs: number | BN; + vestingPeriod: number | BN; + oracleType?: OracleTypeName; + minPrice: number; + maxPrice: number; + minPercentage: number; + maxPercentage: number; + tickSize: number; + skipInitial: boolean; + isMemoRequired: boolean; +} + +export interface IFundLaunchpad extends ILaunchpad, ITokenProgram { + amount: BN; +} + +export interface IDeposit extends ILaunchpad, IOwner, ITokenProgram { + amount: BN; + autoCap?: boolean; + memo?: string; +} + +export interface IClaimDeposits extends ILaunchpad, ITokenProgram {} + +export interface IClaimAllocatedVested extends ILaunchpad, IOwner, ITokenProgram {} + +export interface IClaimAllocatedInstant extends ILaunchpad, IOwner, ITokenProgram {} diff --git a/packages/launchpad/tsconfig.cjs.json b/packages/launchpad/tsconfig.cjs.json new file mode 100644 index 0000000..f6fe01b --- /dev/null +++ b/packages/launchpad/tsconfig.cjs.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.cjs.json", + "compilerOptions": { + "outDir": "./dist/cjs" + } +} diff --git a/packages/launchpad/tsconfig.esm.json b/packages/launchpad/tsconfig.esm.json new file mode 100644 index 0000000..22194d1 --- /dev/null +++ b/packages/launchpad/tsconfig.esm.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.esm.json", + "compilerOptions": { + "outDir": "./dist/esm" + } +} diff --git a/packages/launchpad/typedoc.json b/packages/launchpad/typedoc.json new file mode 100644 index 0000000..6f4e66d --- /dev/null +++ b/packages/launchpad/typedoc.json @@ -0,0 +1,4 @@ +{ + "extends": ["../../typedoc.base.json"], + "entryPoints": ["./index.ts", "./solana/index.ts"] +} diff --git a/packages/staking/package.json b/packages/staking/package.json index 6706f56..252ce62 100644 --- a/packages/staking/package.json +++ b/packages/staking/package.json @@ -1,6 +1,6 @@ { "name": "@streamflow/staking", - "version": "7.0.3", + "version": "7.1.0-alpha.1", "description": "JavaScript SDK to interact with Streamflow Staking protocol.", "homepage": "https://github.com/streamflow-finance/js-sdk/", "main": "dist/esm/index.js", @@ -35,7 +35,7 @@ }, "dependencies": { "@coral-xyz/anchor": "^0.30.0", - "@coral-xyz/borsh": "^0.29.0", + "@coral-xyz/borsh": "^0.30.1", "@solana/buffer-layout": "4.0.1", "@solana/spl-token": "0.4.9", "@solana/wallet-adapter-base": "0.9.19", diff --git a/packages/stream/package.json b/packages/stream/package.json index ed6cbc7..7600d74 100644 --- a/packages/stream/package.json +++ b/packages/stream/package.json @@ -1,6 +1,6 @@ { "name": "@streamflow/stream", - "version": "7.0.3", + "version": "7.1.0-alpha.1", "description": "JavaScript SDK to interact with Streamflow protocol.", "homepage": "https://github.com/streamflow-finance/js-sdk/", "main": "./dist/esm/index.js", @@ -51,7 +51,7 @@ "typescript": "^5.6.3" }, "dependencies": { - "@coral-xyz/anchor": "^0.30.0", + "@coral-xyz/anchor": "^0.30.1", "@coral-xyz/borsh": "0.30.1", "@manahippo/aptos-wallet-adapter": "1.0.10", "@mysten/sui.js": "0.54.1", diff --git a/packages/stream/solana/descriptor/idl/streamflow_aligned_unlocks.json b/packages/stream/solana/descriptor/idl/streamflow_aligned_unlocks.json index ea7162f..9ae17e1 100644 --- a/packages/stream/solana/descriptor/idl/streamflow_aligned_unlocks.json +++ b/packages/stream/solana/descriptor/idl/streamflow_aligned_unlocks.json @@ -2,7 +2,7 @@ "address": "aSTRM2NKoKxNnkmLWk9sz3k74gKBk9t7bpPrTGxMszH", "metadata": { "name": "streamflow_aligned_unlocks", - "version": "1.0.0", + "version": "1.1.0", "spec": "0.1.0", "description": "Proxy to update unlock amount within Streamflow vesting protocol according to Token performance and other metrics" }, @@ -438,6 +438,14 @@ 119 ], "accounts": [ + { + "name": "payer", + "docs": [ + "Rent payer" + ], + "writable": true, + "signer": true + }, { "name": "sender", "writable": true, @@ -446,7 +454,7 @@ { "name": "sender_tokens", "docs": [ - "Associated token account address of `sender`." + "Associated token account address of `payer`." ], "writable": true, "pda": { @@ -660,65 +668,7 @@ "writable": true }, { - "name": "partner", - "writable": true - }, - { - "name": "partner_tokens", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "partner" - }, - { - "kind": "account", - "path": "token_program" - }, - { - "kind": "account", - "path": "mint" - } - ], - "program": { - "kind": "const", - "value": [ - 140, - 151, - 37, - 143, - 78, - 36, - 137, - 241, - 187, - 61, - 16, - 41, - 20, - 142, - 13, - 131, - 11, - 90, - 19, - 153, - 218, - 255, - 16, - 132, - 4, - 142, - 123, - 216, - 219, - 233, - 248, - 89 - ] - } - } + "name": "partner" }, { "name": "mint" diff --git a/packages/stream/solana/descriptor/streamflow_aligned_unlocks.ts b/packages/stream/solana/descriptor/streamflow_aligned_unlocks.ts index a3346ff..d0a1e34 100644 --- a/packages/stream/solana/descriptor/streamflow_aligned_unlocks.ts +++ b/packages/stream/solana/descriptor/streamflow_aligned_unlocks.ts @@ -8,7 +8,7 @@ export type StreamflowAlignedUnlocks = { address: "aSTRM2NKoKxNnkmLWk9sz3k74gKBk9t7bpPrTGxMszH"; metadata: { name: "streamflowAlignedUnlocks"; - version: "1.0.0"; + version: "1.1.0"; spec: "0.1.0"; description: "Proxy to update unlock amount within Streamflow vesting protocol according to Token performance and other metrics"; }; @@ -395,6 +395,12 @@ export type StreamflowAlignedUnlocks = { name: "create"; discriminator: [24, 30, 200, 40, 5, 28, 7, 119]; accounts: [ + { + name: "payer"; + docs: ["Rent payer"]; + writable: true; + signer: true; + }, { name: "sender"; writable: true; @@ -402,7 +408,7 @@ export type StreamflowAlignedUnlocks = { }, { name: "senderTokens"; - docs: ["Associated token account address of `sender`."]; + docs: ["Associated token account address of `payer`."]; writable: true; pda: { seeds: [ @@ -607,64 +613,6 @@ export type StreamflowAlignedUnlocks = { }, { name: "partner"; - writable: true; - }, - { - name: "partnerTokens"; - writable: true; - pda: { - seeds: [ - { - kind: "account"; - path: "partner"; - }, - { - kind: "account"; - path: "tokenProgram"; - }, - { - kind: "account"; - path: "mint"; - }, - ]; - program: { - kind: "const"; - value: [ - 140, - 151, - 37, - 143, - 78, - 36, - 137, - 241, - 187, - 61, - 16, - 41, - 20, - 142, - 13, - 131, - 11, - 90, - 19, - 153, - 218, - 255, - 16, - 132, - 4, - 142, - 123, - 216, - 219, - 233, - 248, - 89, - ]; - }; - }; }, { name: "mint"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17045fd..6108b1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -82,7 +82,7 @@ importers: packages/distributor: dependencies: '@coral-xyz/anchor': - specifier: ^0.30.0 + specifier: ^0.30.1 version: 0.30.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) '@coral-xyz/borsh': specifier: 0.30.1 @@ -153,14 +153,63 @@ importers: specifier: ^3.2.5 version: 3.2.5 + packages/launchpad: + dependencies: + '@coral-xyz/anchor': + specifier: ^0.30.1 + version: 0.30.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@coral-xyz/borsh': + specifier: ^0.30.1 + version: 0.30.1(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@solana/buffer-layout': + specifier: 4.0.1 + version: 4.0.1 + '@solana/spl-token': + specifier: 0.4.9 + version: 0.4.9(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': + specifier: 0.9.19 + version: 0.9.19(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@solana/web3.js': + specifier: 1.95.4 + version: 1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@streamflow/common': + specifier: workspace:* + version: link:../common + '@streamflow/stream': + specifier: workspace:* + version: link:../stream + bn.js: + specifier: 5.2.1 + version: 5.2.1 + borsh: + specifier: ^2.0.0 + version: 2.0.0 + bs58: + specifier: 5.0.0 + version: 5.0.0 + p-queue: + specifier: ^8.0.1 + version: 8.0.1 + devDependencies: + '@streamflow/eslint-config': + specifier: workspace:* + version: link:../eslint-config + '@types/bn.js': + specifier: 5.1.1 + version: 5.1.1 + typescript: + specifier: ^5.6.3 + version: 5.6.3 + packages/staking: dependencies: '@coral-xyz/anchor': specifier: ^0.30.0 version: 0.30.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) '@coral-xyz/borsh': - specifier: ^0.29.0 - version: 0.29.0(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + specifier: ^0.30.1 + version: 0.30.1(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/buffer-layout': specifier: 4.0.1 version: 4.0.1 @@ -202,7 +251,7 @@ importers: packages/stream: dependencies: '@coral-xyz/anchor': - specifier: ^0.30.0 + specifier: ^0.30.1 version: 0.30.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) '@coral-xyz/borsh': specifier: 0.30.1 @@ -969,12 +1018,6 @@ packages: resolution: {integrity: sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ==} engines: {node: '>=11'} - '@coral-xyz/borsh@0.29.0': - resolution: {integrity: sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ==} - engines: {node: '>=10'} - peerDependencies: - '@solana/web3.js': ^1.68.0 - '@coral-xyz/borsh@0.30.1': resolution: {integrity: sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ==} engines: {node: '>=10'} @@ -6724,7 +6767,7 @@ snapshots: dependencies: '@coral-xyz/anchor-errors': 0.30.1 '@coral-xyz/borsh': 0.30.1(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) - '@noble/hashes': 1.3.3 + '@noble/hashes': 1.5.0 '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) bn.js: 5.2.1 bs58: 4.0.1 @@ -6742,12 +6785,6 @@ snapshots: - encoding - utf-8-validate - '@coral-xyz/borsh@0.29.0(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))': - dependencies: - '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) - bn.js: 5.2.1 - buffer-layout: 1.2.2 - '@coral-xyz/borsh@0.30.1(@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))': dependencies: '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) @@ -9182,7 +9219,7 @@ snapshots: dot-case@3.0.4: dependencies: no-case: 3.0.4 - tslib: 2.6.2 + tslib: 2.8.1 dot-prop@5.3.0: dependencies: @@ -10678,7 +10715,7 @@ snapshots: lower-case@2.0.2: dependencies: - tslib: 2.6.2 + tslib: 2.8.1 lowercase-keys@2.0.0: {} @@ -10950,7 +10987,7 @@ snapshots: no-case@3.0.4: dependencies: lower-case: 2.0.2 - tslib: 2.6.2 + tslib: 2.8.1 node-addon-api@2.0.2: {} @@ -11821,7 +11858,7 @@ snapshots: snake-case@3.0.4: dependencies: dot-case: 3.0.4 - tslib: 2.6.2 + tslib: 2.8.1 socks-proxy-agent@7.0.0: dependencies: