From cb3a4eda3f0ab22653e4242886a2d11b9f6bb22c Mon Sep 17 00:00:00 2001 From: Adam Lawrence Date: Thu, 12 May 2022 14:39:22 -0400 Subject: [PATCH] Integration: Castle deposit and withdraw (#661) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * npm: bump @solana/spl-token from 0.1.8 to 0.2.0 (#2) Bumps [@solana/spl-token](https://github.com/solana-labs/solana-program-library) from 0.1.8 to 0.2.0. - [Release notes](https://github.com/solana-labs/solana-program-library/releases) - [Commits](https://github.com/solana-labs/solana-program-library/compare/@solana/spl-token@v0.1.8...stake-pool-v0.2.0) --- updated-dependencies: - dependency-name: "@solana/spl-token" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * npm: bump tsconfig-paths from 3.12.0 to 3.14.1 (#3) Bumps [tsconfig-paths](https://github.com/dividab/tsconfig-paths) from 3.12.0 to 3.14.1. - [Release notes](https://github.com/dividab/tsconfig-paths/releases) - [Changelog](https://github.com/dividab/tsconfig-paths/blob/master/CHANGELOG.md) - [Commits](https://github.com/dividab/tsconfig-paths/compare/v3.12.0...v3.14.1) --- updated-dependencies: - dependency-name: tsconfig-paths dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * npm: bump next from 12.1.4 to 12.1.5 (#4) Bumps [next](https://github.com/vercel/next.js) from 12.1.4 to 12.1.5. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v12.1.4...v12.1.5) --- updated-dependencies: - dependency-name: next dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * npm: bump dayjs from 1.10.7 to 1.11.1 (#6) Bumps [dayjs](https://github.com/iamkun/dayjs) from 1.10.7 to 1.11.1. - [Release notes](https://github.com/iamkun/dayjs/releases) - [Changelog](https://github.com/iamkun/dayjs/blob/dev/CHANGELOG.md) - [Commits](https://github.com/iamkun/dayjs/compare/v1.10.7...v1.11.1) --- updated-dependencies: - dependency-name: dayjs dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * npm: bump @typescript-eslint/eslint-plugin from 5.19.0 to 5.20.0 (#7) Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.19.0 to 5.20.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.20.0/packages/eslint-plugin) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * npm: bump @blockworks-foundation/mango-client from 3.4.3 to 3.4.4 (#9) Bumps [@blockworks-foundation/mango-client](https://github.com/blockworks-foundation/mango-client-v3) from 3.4.3 to 3.4.4. - [Release notes](https://github.com/blockworks-foundation/mango-client-v3/releases) - [Commits](https://github.com/blockworks-foundation/mango-client-v3/commits) --- updated-dependencies: - dependency-name: "@blockworks-foundation/mango-client" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Integration: Castle deposit and withdraw (#13) * Add @castlefinance/vault-sdk dependency * Add CastleDepositForm interface + Instructions.DepositIntoCastle to proposalCreationTypes * Add Instructions.DepositIntoCastle to hooks/useGovernanceAssets.ts * Add getCastleDepositInstruction to instructionTools.ts * Add getCastleDepositSchema to utils/validations * Add CastleDeposit component * Add CastleDeposi to New Proposal component * Fixed eslint errors * execution option button * Implement execution option * fix - lint error * Fix formatting divergence * Revert formatting updates * Undo formatting changes? * Add deposit into tx without refresh * Integrate updated sdk * Add Castle Withdraw initial files * Implement basic withdraw, deposit works on mainnet * Temporarily remove git hooks * Integrate vault-core * Remove old config code from CastleWithdraw * Grab amount from instruction data * Remove unused param * Fix duplicate cases * Remove duplicate case * temporarily disable husky * Clean up * Update ExecuteInstructionButton.tsx * fix nft amount match (#640) * fix * fix * filter fix (#641) * Feature/release loader and council (#643) * release loader * hide multi vote for nfts * vote with council alone when plugin is on * fix council * release nfts addon * Add Foresight DAO (#642) * add foresight-sdk dep * add foresight to devnet.json * fix programId * add FORE mint to MINT_METADATA * add example instruction to form * remove comma * add MakeInitMarketParams for Foresight * husky did this * lint * more lin * more foresight instructions * lint * tidy up provider * lint * add initCategory for foresight * useGovernanceAssets * missing import * use DEVNET_TREASURY const * add ForesightMakeResolveMarketParams * finish adding resolve market * avoid repeated constant * missing resolve market in useGovernanceAssets * clean up foresight instructions * add AddMarketListToCategory * add ForesightAddMarketListToCategory * update foresight-sdk * add AddMarketMetadata * add ForesightGovernedAccountSelect * start using ForesightGovernedAccountSelect * reduce type duplication * save unsaved file * save unsaved file * remove unused imports * more refactoring to avoid duplication * fix empty object type * fix empty object type * fix empty object type * unused imports * fix types import * more refactoring * unused import * extract ForesightUseEffects * unused import * refactor more * unused import * refactor addmarketmetadataparams * refactor init category * refactor MakeInitMarketList * big refactor * lint * more refactoring * lint * try using the user wallet as an authority * change other stuff (except resolving markets) to user wallet * missing param * reorder element * reorder elements * update for newer foresight-sdk * remove unused import * fix issue with wrong program id * bump foresight-sdk version * npm: bump @blockworks-foundation/mango-client from 3.4.3 to 3.4.4 (#2) Bumps [@blockworks-foundation/mango-client](https://github.com/blockworks-foundation/mango-client-v3) from 3.4.3 to 3.4.4. - [Release notes](https://github.com/blockworks-foundation/mango-client-v3/releases) - [Commits](https://github.com/blockworks-foundation/mango-client-v3/commits) --- updated-dependencies: - dependency-name: "@blockworks-foundation/mango-client" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * npm: bump @friktion-labs/friktion-sdk from 1.1.25 to 1.1.70 (#3) Bumps [@friktion-labs/friktion-sdk](https://github.com/Friktion-Labs/volt) from 1.1.25 to 1.1.70. - [Release notes](https://github.com/Friktion-Labs/volt/releases) - [Commits](https://github.com/Friktion-Labs/volt/commits) --- updated-dependencies: - dependency-name: "@friktion-labs/friktion-sdk" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * npm: bump tsconfig-paths from 3.12.0 to 3.14.1 (#5) Bumps [tsconfig-paths](https://github.com/dividab/tsconfig-paths) from 3.12.0 to 3.14.1. - [Release notes](https://github.com/dividab/tsconfig-paths/releases) - [Changelog](https://github.com/dividab/tsconfig-paths/blob/master/CHANGELOG.md) - [Commits](https://github.com/dividab/tsconfig-paths/compare/v3.12.0...v3.14.1) --- updated-dependencies: - dependency-name: tsconfig-paths dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * npm: bump @solana/spl-token-registry from 0.2.3470 to 0.2.3635 (#6) Bumps [@solana/spl-token-registry](https://github.com/solana-labs/token-list) from 0.2.3470 to 0.2.3635. - [Release notes](https://github.com/solana-labs/token-list/releases) - [Changelog](https://github.com/solana-labs/token-list/blob/main/CHANGELOG.md) - [Commits](https://github.com/solana-labs/token-list/compare/v0.2.3470...v0.2.3635) --- updated-dependencies: - dependency-name: "@solana/spl-token-registry" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * npm: bump postcss from 8.4.4 to 8.4.12 (#10) Bumps [postcss](https://github.com/postcss/postcss) from 8.4.4 to 8.4.12. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.4.4...8.4.12) --- updated-dependencies: - dependency-name: postcss dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * update lock file * npm: bump @solana/spl-token from 0.1.8 to 0.2.0 (#7) Bumps [@solana/spl-token](https://github.com/solana-labs/solana-program-library) from 0.1.8 to 0.2.0. - [Release notes](https://github.com/solana-labs/solana-program-library/releases) - [Commits](https://github.com/solana-labs/solana-program-library/compare/@solana/spl-token@v0.1.8...stake-pool-v0.2.0) --- updated-dependencies: - dependency-name: "@solana/spl-token" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * npm: bump axios from 0.26.1 to 0.27.1 (#4) Bumps [axios](https://github.com/axios/axios) from 0.26.1 to 0.27.1. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v0.26.1...v0.27.1) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * switch to AssetAccount * fix friktion-sdk version in line with main * remove accidental package.json entries * get rid of package.json deviations * update lock file * npm: bump next from 12.1.4 to 12.1.5 (#12) Bumps [next](https://github.com/vercel/next.js) from 12.1.4 to 12.1.5. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v12.1.4...v12.1.5) --- updated-dependencies: - dependency-name: next dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * npm: bump react from 18.0.0 to 18.1.0 (#13) Bumps [react](https://github.com/facebook/react/tree/HEAD/packages/react) from 18.0.0 to 18.1.0. - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v18.1.0/packages/react) --- updated-dependencies: - dependency-name: react dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * npm: bump ts-node from 10.5.0 to 10.7.0 (#14) Bumps [ts-node](https://github.com/TypeStrong/ts-node) from 10.5.0 to 10.7.0. - [Release notes](https://github.com/TypeStrong/ts-node/releases) - [Commits](https://github.com/TypeStrong/ts-node/compare/v10.5.0...v10.7.0) --- updated-dependencies: - dependency-name: ts-node dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * npm: bump @typescript-eslint/eslint-plugin from 5.19.0 to 5.21.0 (#16) Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.19.0 to 5.21.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.21.0/packages/eslint-plugin) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix friktion-sdk version in lock file * add foresight to devnet.json * move foresight utils to utils dir Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix type errors, updated packages * auxliary token accounts (#644) * auxliary token accounts * params page * revert accounts from params page * fixes * fix * fix auxiliary accounts display * fix; * Feature/anchor npm upgrade (#646) * anchor npm upgrade * instructions change wip * upgrade instructions to use new syntax anchor * fix decode * small my proposals fix * fix vote records * fix * commit yarn lock * Add NFTClubBerlin Multisig (#648) * Add trade with serum (#583) * setup framework for token actions get proposal and TX simulation working as expected add some more complex validations remove unused vars change verbage remove form field for deposit address and use ATA bump package version * devs doin stuff * clean up * tweak trade on serum button * use pre-requisite instruction to avoid TX limit * add slight disclaimer and link to Serum remote * fix nfts var * fix double send button; restrict trade on serum to SPL tokens * Fix logo path and update info (#649) * FIRST COMMINT LOGO AND INFO NFT OFF ROAD FAN CLUB DAO * FIX LOGO AND INFO NFT OFF ROAD FAN CLUB DAO * 'Token' on About tab should show mint rather than realm pubkey (#651) * Token should show mint rather than realm pubkey * Only show token symbol if realm is certified * transaction timeouts based on blocks (#653) * send transction v2 player * timeout based on block height * forward function + comments * fix * reject all on timeout * pull vaults from api, render execution option based on program parsing * Fix CI issue * Use castle dependency for anchor * Fix type errors Co-authored-by: Alexander Schwartzberg Co-authored-by: Adrian Brzeziński Co-authored-by: Kevin Heavey <24635973+kevinheavey@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chris Nagy <20590073+what-name@users.noreply.github.com> Co-authored-by: Tommy Johnson <32071703+tomjohn1028@users.noreply.github.com> Co-authored-by: Xavier Baquero <93201271+axbaquero@users.noreply.github.com> Co-authored-by: Evan Doyle * Update yarn.lock * Add back husky * sleep-gap send, remove dead code, use getters * Use try/catch for ATA creation * Rename execution option buttons * Update instructionTools.ts * Implement generic execution options Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alexander Schwartzberg Co-authored-by: Adrian Brzeziński Co-authored-by: Kevin Heavey <24635973+kevinheavey@users.noreply.github.com> Co-authored-by: Chris Nagy <20590073+what-name@users.noreply.github.com> Co-authored-by: Tommy Johnson <32071703+tomjohn1028@users.noreply.github.com> Co-authored-by: Xavier Baquero <93201271+axbaquero@users.noreply.github.com> Co-authored-by: Evan Doyle --- @types/types.ts | 3 +- actions/executeTransaction.ts | 84 +++- components/InstructionOptions.tsx | 78 +++ .../instructions/ExecuteInstructionButton.tsx | 54 +- components/instructions/instructionCard.tsx | 40 +- components/instructions/tools.tsx | 7 + hooks/useGovernanceAssets.ts | 10 + package.json | 7 +- .../instructions/Castle/CastleDeposit.tsx | 197 ++++++++ .../instructions/Castle/CastleWithdraw.tsx | 197 ++++++++ pages/dao/[symbol]/proposal/new.tsx | 19 +- utils/connection.ts | 14 + utils/instructionTools.ts | 177 +------ utils/instructions/Castle/index.ts | 405 +++++++++++++++ utils/send.tsx | 133 ++++- utils/uiTypes/proposalCreationTypes.ts | 18 + utils/validations.tsx | 53 ++ yarn.lock | 476 ++++++++++++++++-- 18 files changed, 1715 insertions(+), 257 deletions(-) create mode 100644 components/InstructionOptions.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Castle/CastleDeposit.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Castle/CastleWithdraw.tsx create mode 100644 utils/instructions/Castle/index.ts diff --git a/@types/types.ts b/@types/types.ts index a9c06fd276..e09141d5af 100644 --- a/@types/types.ts +++ b/@types/types.ts @@ -1,7 +1,8 @@ +import { EndpointTypes } from '@models/types' import type { AccountInfo, PublicKey } from '@solana/web3.js' export interface EndpointInfo { - name: string + name: EndpointTypes url: string } diff --git a/actions/executeTransaction.ts b/actions/executeTransaction.ts index f774452533..884ca23e0b 100644 --- a/actions/executeTransaction.ts +++ b/actions/executeTransaction.ts @@ -1,20 +1,34 @@ import { Keypair, Transaction, TransactionInstruction } from '@solana/web3.js' import { - getGovernanceProgramVersion, + sendSignedAndAdjacentTransactions, + sendTransaction, + signTransactions, +} from '@utils/send' +import Wallet from '@project-serum/sol-wallet-adapter' +import { + RpcContext, Proposal, ProposalTransaction, + getGovernanceProgramVersion, + withExecuteTransaction, + ProgramAccount, } from '@solana/spl-governance' -import { withExecuteTransaction } from '@solana/spl-governance' -import { RpcContext } from '@solana/spl-governance' -import { ProgramAccount } from '@solana/spl-governance' -import { sendTransaction } from '@utils/send' - +/** + * Executes a proposal transaction + * @param rpcContext RPC contextual information + * @param proposal Metadata about the proposal + * @param instruction Instruction that will be executed by the proposal + * @param adjacentTransaction Optional transaction that is sent in the same slot as the proposal instruction. + * @param preExecutionTransactions Optional tansactions that are executed before the proposal instruction + */ export const executeTransaction = async ( { connection, wallet, programId }: RpcContext, proposal: ProgramAccount, - instruction: ProgramAccount + instruction: ProgramAccount, + adjacentTransaction?: Transaction, + preExecutionTransactions?: Transaction[] ) => { const signers: Keypair[] = [] const instructions: TransactionInstruction[] = [] @@ -36,16 +50,52 @@ export const executeTransaction = async ( [instruction.account.getSingleInstruction()] ) - const transaction = new Transaction() + // Create proposal transaction + const proposalTransaction = new Transaction().add(...instructions) - transaction.add(...instructions) + // Sign and send all pre-execution transactions + if (preExecutionTransactions && !preExecutionTransactions?.length) { + await Promise.all( + preExecutionTransactions.map((transaction) => + sendTransaction({ + transaction, + wallet, + connection, + sendingMessage: 'Sending pre-execution transaction', + successMessage: 'Sent pre-execution transaction', + }) + ) + ) + console.log('sent preExecutionTransactions', preExecutionTransactions) + } - await sendTransaction({ - transaction, - wallet, - connection, - signers, - sendingMessage: 'Executing instruction', - successMessage: 'Execution finalized', - }) + // Some proposals require additional adjacent transactions due to tx size limits + if (adjacentTransaction) { + const [signedProposalTx, signedAdjacentTx] = await signTransactions({ + transactionsAndSigners: [ + { transaction: proposalTransaction }, + { transaction: adjacentTransaction }, + ], + wallet: (wallet as unknown) as Wallet, + connection, + }) + // Send proposal transaction with prepended adjacent transaction + await sendSignedAndAdjacentTransactions({ + signedTransaction: signedProposalTx, + adjacentTransaction: signedAdjacentTx, + connection, + sendingMessage: 'Executing instruction', + successMessage: 'Execution finalized', + }) + } else { + // Send the proposal transaction + await sendTransaction({ + transaction: proposalTransaction, + wallet, + connection, + signers, + sendingMessage: 'Executing instruction', + successMessage: 'Execution finalized', + }) + } } diff --git a/components/InstructionOptions.tsx b/components/InstructionOptions.tsx new file mode 100644 index 0000000000..1ddbc5f40f --- /dev/null +++ b/components/InstructionOptions.tsx @@ -0,0 +1,78 @@ +import { Fragment } from 'react' +import { Listbox, Transition } from '@headlessui/react' +import { SelectorIcon } from '@heroicons/react/solid' + +// Add more options as needed +export enum InstructionOptions { + none = 'none', + castleRefresh = 'Castle: Refresh', + castleReconcileRefresh = 'Castle: Reconcile and Refresh', +} + +export type InstructionOption = `${InstructionOptions}` + +// Mapping between listbox option label and underlying option +const executionOptions: { label: string; value: InstructionOption }[] = [ + { label: 'Select Option', value: InstructionOptions.none }, + { label: 'Castle: Deposit', value: InstructionOptions.castleRefresh }, + { + label: 'Castle: Withdraw', + value: InstructionOptions.castleReconcileRefresh, + }, +] + +export default function InstructionOptionInput(props: { + value: InstructionOption + setValue: (updatedValue: InstructionOption) => void +}) { + const { value, setValue } = props + + const selectedOption = executionOptions.find((o) => o.value === value) + + return ( + +
+ + {selectedOption?.label} + + + + + + + {executionOptions.map((option, optionIdx) => ( + + `cursor-default select-none relative py-2 px-4 ${ + active + ? 'text-amber-900 bg-amber-100' + : 'text-primary-light' + }` + } + value={option.value} + > + {({ selected }) => ( + <> + + {option.label} + + + )} + + ))} + + +
+
+ ) +} diff --git a/components/instructions/ExecuteInstructionButton.tsx b/components/instructions/ExecuteInstructionButton.tsx index 0edb78e20d..2bba1f8060 100644 --- a/components/instructions/ExecuteInstructionButton.tsx +++ b/components/instructions/ExecuteInstructionButton.tsx @@ -13,15 +13,24 @@ import { RpcContext } from '@solana/spl-governance' import useRealm from '@hooks/useRealm' import useWalletStore from 'stores/useWalletStore' import { ProgramAccount } from '@solana/spl-governance' -import { PublicKey } from '@solana/web3.js' +import { PublicKey, Transaction } from '@solana/web3.js' import Tooltip from '@components/Tooltip' import { getProgramVersionForRealm } from '@models/registry/api' import { notify } from '@utils/notifications' +import { + InstructionOption, + InstructionOptions, +} from '@components/InstructionOptions' import dayjs from 'dayjs' import { getFormattedStringFromDays, SECS_PER_DAY, } from 'VoteStakeRegistry/tools/dateTools' +import { + getCastleReconcileInstruction, + getCastleRefreshInstruction, +} from '@utils/instructions/Castle' +import Wallet from '@project-serum/sol-wallet-adapter' export enum PlayState { Played, @@ -35,11 +44,13 @@ export function ExecuteInstructionButton({ playing, setPlaying, proposalInstruction, + instructionOption, }: { proposal: ProgramAccount proposalInstruction: ProgramAccount playing: PlayState setPlaying: React.Dispatch> + instructionOption: InstructionOption }) { const { realmInfo } = useRealm() const wallet = useWalletStore((s) => s.current) @@ -79,7 +90,46 @@ export function ExecuteInstructionButton({ setPlaying(PlayState.Playing) try { - await executeTransaction(rpcContext, proposal, proposalInstruction) + let preExecutionTransactions: Transaction[] | undefined = undefined + let adjacentTransaction: Transaction | undefined = undefined + + // Depending on the instruction option, add the appropriate pre-execution + // and adjacent transactions to the proposal execution + switch (instructionOption) { + case InstructionOptions.castleRefresh: + adjacentTransaction = new Transaction().add( + await getCastleRefreshInstruction( + rpcContext.connection, + (wallet as unknown) as Wallet, + proposalInstruction + ) + ) + break + case InstructionOptions.castleReconcileRefresh: { + preExecutionTransactions = await getCastleReconcileInstruction( + rpcContext.connection, + (wallet as unknown) as Wallet, + proposalInstruction + ) + adjacentTransaction = new Transaction().add( + await getCastleRefreshInstruction( + rpcContext.connection, + (wallet as unknown) as Wallet, + proposalInstruction + ) + ) + break + } + } + + await executeTransaction( + rpcContext, + proposal, + proposalInstruction, + adjacentTransaction, + preExecutionTransactions + ) + await refetchProposals() } catch (error) { notify({ type: 'error', message: `error executing instruction ${error}` }) diff --git a/components/instructions/instructionCard.tsx b/components/instructions/instructionCard.tsx index 0bde0c90f2..cc3fc37e72 100644 --- a/components/instructions/instructionCard.tsx +++ b/components/instructions/instructionCard.tsx @@ -6,6 +6,7 @@ import { ProposalTransaction, } from '@solana/spl-governance' import { + ALL_CASTLE_PROGRAMS, getAccountName, getInstructionDescriptor, InstructionDescriptor, @@ -24,6 +25,10 @@ import { Metadata } from '@metaplex-foundation/mpl-token-metadata' import axios from 'axios' import useGovernanceAssets from '@hooks/useGovernanceAssets' import tokenService from '@utils/services/token' +import InstructionOptionInput, { + InstructionOption, + InstructionOptions, +} from '@components/InstructionOptions' export default function InstructionCard({ index, @@ -41,6 +46,10 @@ export default function InstructionCard({ const connection = useWalletStore((s) => s.connection) const tokenRecords = useWalletStore((s) => s.selectedRealm) const [descriptor, setDescriptor] = useState() + const [instructionOption, setInstructionOption] = useState( + InstructionOptions.none + ) + const [playing, setPlaying] = useState( proposalInstruction.account.executedAt ? PlayState.Played @@ -48,6 +57,11 @@ export default function InstructionCard({ ) const [nftImgUrl, setNftImgUrl] = useState('') const [tokenImgUrl, setTokenImgUrl] = useState('') + + const allProposalPrograms = proposalInstruction.account.instructions + ?.map((i) => i.programId.toBase58()) + .flat() + useEffect(() => { getInstructionDescriptor( connection, @@ -165,12 +179,26 @@ export default function InstructionCard({ /> {proposal && ( - + + + {/* Show execution option if the proposal contains a specified program id and + proposal has not executed already. */} + {allProposalPrograms?.filter((a) => + ALL_CASTLE_PROGRAMS.map((a) => a.toBase58()).includes(a) + ).length > 0 && + playing != PlayState.Played && ( + + )} + )} diff --git a/components/instructions/tools.tsx b/components/instructions/tools.tsx index 07c420d4c5..fff53fe18a 100644 --- a/components/instructions/tools.tsx +++ b/components/instructions/tools.tsx @@ -21,6 +21,7 @@ import { ATA_PROGRAM_INSTRUCTIONS } from './programs/associatedTokenAccount' import { governance as foresightGov } from '@foresight-tmp/foresight-sdk' import { ConnectionContext } from '@utils/connection' import { NFT_VOTER_INSTRUCTIONS } from './programs/nftVotingClient' +import { PROGRAM_IDS } from '@castlefinance/vault-sdk' /** * Default governance program id instance */ @@ -199,6 +200,12 @@ export const AUXILIARY_TOKEN_ACCOUNTS = { export const HIDDEN_TREASURES = [...HIDDEN_MNGO_TREASURES] +export const ALL_CASTLE_PROGRAMS = [ + PROGRAM_IDS['devnet-parity'], + PROGRAM_IDS['devnet-staging'], + PROGRAM_IDS['mainnet'], +] + export interface AccountDescriptor { name: string important?: boolean diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index ac6ade93ff..26d9276053 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -190,6 +190,16 @@ export default function useGovernanceAssets() { name: 'Execute Custom Instruction', isVisible: canUseAnyInstruction, }, + { + id: Instructions.DepositIntoCastle, + name: 'Castle: Deposit into Vault', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.WithrawFromCastle, + name: 'Castle: Withdraw from Vault', + isVisible: canUseAnyInstruction, + }, { id: Instructions.DepositIntoVolt, name: 'Friktion: Deposit into Volt', diff --git a/package.json b/package.json index 5537152373..aab64ccfcd 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,8 @@ "@blockworks-foundation/mango-client": "^3.4.3", "@blockworks-foundation/voter-stake-registry-client": "^0.2.3", "@cardinal/namespaces-components": "^2.5.5", + "@castlefinance/vault-core": "^0.1.3", + "@castlefinance/vault-sdk": "^2.1.2", "@dialectlabs/react": "0.5.1", "@dialectlabs/react-ui": "0.8.2", "@emotion/react": "^11.9.0", @@ -38,15 +40,16 @@ "@metaplex-foundation/mpl-token-metadata": "^1.2.5", "@mithraic-labs/serum-remote": "^0.0.1-rc.16", "@next/bundle-analyzer": "^12.1.5", - "@mithraic-labs/serum-remote": "^0.0.1-rc.16", "@nfteyez/sol-rayz": "^0.10.2", "@nivo/bar": "^0.79.1", "@nivo/core": "^0.79.0", "@notifi-network/notifi-react-hooks": "^0.12.1", "@project-serum/anchor": "^0.24.2", + "@project-serum/borsh": "^0.2.5", "@project-serum/common": "^0.0.1-beta.3", "@project-serum/serum": "^0.13.61", "@project-serum/sol-wallet-adapter": "^0.2.6", + "@solana/buffer-layout": "^4.0.0", "@sentry/nextjs": "^6.19.7", "@solana/governance-program-library": "^0.15.2", "@solana/spl-governance": "^0.0.34", @@ -63,7 +66,7 @@ "bignumber.js": "^9.0.2", "buffer-layout": "^1.2.2", "classnames": "^2.3.1", - "dayjs": "^1.10.7", + "dayjs": "^1.11.1", "immer": "^9.0.12", "next": "^12.1.5", "next-themes": "^0.1.1", diff --git a/pages/dao/[symbol]/proposal/components/instructions/Castle/CastleDeposit.tsx b/pages/dao/[symbol]/proposal/components/instructions/Castle/CastleDeposit.tsx new file mode 100644 index 0000000000..b012e2ac2d --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Castle/CastleDeposit.tsx @@ -0,0 +1,197 @@ +import React, { useContext, useEffect, useState } from 'react' +import Input from '@components/inputs/Input' +import useRealm from '@hooks/useRealm' +import { getMintMinAmountAsDecimal } from '@tools/sdk/units' +import { PublicKey } from '@solana/web3.js' +import { precision } from '@utils/formatting' +import useWalletStore from 'stores/useWalletStore' +import { + CastleDepositForm, + UiInstruction, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../../new' +import { getCastleDepositSchema } from '@utils/validations' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import { Governance } from '@solana/spl-governance' +import { ProgramAccount } from '@solana/spl-governance' +import GovernedAccountSelect from '../../GovernedAccountSelect' +import Select from '@components/inputs/Select' +import { + Clusters, + DeploymentEnvs, + VaultConfig, +} from '@castlefinance/vault-core' +import { + getCastleVaults, + getCastleDepositInstruction, +} from '@utils/instructions/Castle' + +const CastleDeposit = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const { governedTokenAccountsWithoutNfts } = useGovernanceAssets() + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + + // Store CastleDepositForm state + const [form, setForm] = useState({ + amount: undefined, + governedTokenAccount: undefined, + castleVaultId: '', + programId: programId?.toString(), + mintInfo: undefined, + }) + + const [castleVaults, setCastleVaults] = useState< + VaultConfig[] | null + >(null) + + const [governedAccount, setGovernedAccount] = useState< + ProgramAccount | undefined + >(undefined) + + const [formErrors, setFormErrors] = useState({}) + + const mintMinAmount = form.mintInfo + ? getMintMinAmountAsDecimal(form.mintInfo) + : 1 + + const currentPrecision = precision(mintMinAmount) + const { handleSetInstructions } = useContext(NewProposalContext) + + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + + const setMintInfo = (value) => { + setForm({ ...form, mintInfo: value }) + } + + const setAmount = (event) => { + const value = event.target.value + handleSetForm({ + value: value, + propertyName: 'amount', + }) + } + + const validateAmountOnBlur = () => { + const value = form.amount + + handleSetForm({ + value: parseFloat( + Math.max( + Number(mintMinAmount), + Math.min(Number(Number.MAX_SAFE_INTEGER), Number(value)) + ).toFixed(currentPrecision) + ), + propertyName: 'amount', + }) + } + + async function getInstruction(): Promise { + return await getCastleDepositInstruction({ + schema, + form, + amount: form.amount ?? 0, + programId, + connection, + wallet, + setFormErrors, + }) + } + + // Grab Castle vault information from config server + useEffect(() => { + const getCastleConfig = async () => { + const vaults = (await getCastleVaults()).filter((v) => + connection.cluster == 'mainnet' + ? v.cluster == Clusters.mainnetBeta + : v.cluster == Clusters.devnet + ) + console.log(vaults) + setCastleVaults(vaults) + } + getCastleConfig() + }, [connection.cluster]) + + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + handleSetInstructions( + { governedAccount: governedAccount, getInstruction }, + index + ) + }, [form]) + + useEffect(() => { + setGovernedAccount(form.governedTokenAccount?.governance) + setMintInfo(form.governedTokenAccount?.extensions.mint?.account) + }, [form.governedTokenAccount]) + + const schema = getCastleDepositSchema({ form }) + + return ( + + { + handleSetForm({ value, propertyName: 'governedTokenAccount' }) + }} + value={form.governedTokenAccount} + error={formErrors['governedTokenAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + /> + + + + + ) +} + +export default CastleDeposit diff --git a/pages/dao/[symbol]/proposal/components/instructions/Castle/CastleWithdraw.tsx b/pages/dao/[symbol]/proposal/components/instructions/Castle/CastleWithdraw.tsx new file mode 100644 index 0000000000..d0bcba7961 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Castle/CastleWithdraw.tsx @@ -0,0 +1,197 @@ +import React, { useContext, useEffect, useState } from 'react' +import Input from '@components/inputs/Input' +import useRealm from '@hooks/useRealm' +import { getMintMinAmountAsDecimal } from '@tools/sdk/units' +import { PublicKey } from '@solana/web3.js' +import { precision } from '@utils/formatting' +import useWalletStore from 'stores/useWalletStore' +import { + CastleWithdrawForm, + UiInstruction, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../../new' +import { getCastleWithdrawSchema } from '@utils/validations' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import { Governance } from '@solana/spl-governance' +import { ProgramAccount } from '@solana/spl-governance' +import GovernedAccountSelect from '../../GovernedAccountSelect' +import Select from '@components/inputs/Select' +import { + VaultConfig, + DeploymentEnvs, + Clusters, +} from '@castlefinance/vault-core' +import { + getCastleVaults, + getCastleWithdrawInstruction, +} from '@utils/instructions/Castle' + +const CastleWithdraw = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const { governedTokenAccountsWithoutNfts } = useGovernanceAssets() + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + + // Store CastleWithdrawForm state + const [form, setForm] = useState({ + amount: undefined, + governedTokenAccount: undefined, + castleVaultId: '', + programId: programId?.toString(), + mintInfo: undefined, + }) + + const [castleVaults, setCastleVaults] = useState< + VaultConfig[] | null + >(null) + + const [governedAccount, setGovernedAccount] = useState< + ProgramAccount | undefined + >(undefined) + + const [formErrors, setFormErrors] = useState({}) + + const mintMinAmount = form.mintInfo + ? getMintMinAmountAsDecimal(form.mintInfo) + : 1 + + const currentPrecision = precision(mintMinAmount) + const { handleSetInstructions } = useContext(NewProposalContext) + + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + + const setMintInfo = (value) => { + setForm({ ...form, mintInfo: value }) + } + + const setAmount = (event) => { + const value = event.target.value + handleSetForm({ + value: value, + propertyName: 'amount', + }) + } + + const validateAmountOnBlur = () => { + const value = form.amount + + handleSetForm({ + value: parseFloat( + Math.max( + Number(mintMinAmount), + Math.min(Number(Number.MAX_SAFE_INTEGER), Number(value)) + ).toFixed(currentPrecision) + ), + propertyName: 'amount', + }) + } + + async function getInstruction(): Promise { + return await getCastleWithdrawInstruction({ + schema, + form, + amount: form.amount ?? 0, + programId, + connection, + wallet, + setFormErrors, + }) + } + + // Grab Castle vault information from config server + useEffect(() => { + const getCastleConfig = async () => { + const vaults = (await getCastleVaults()).filter((v) => + connection.cluster == 'mainnet' + ? v.cluster == Clusters.mainnetBeta + : v.cluster == Clusters.devnet + ) + setCastleVaults(vaults) + } + getCastleConfig() + }, []) + + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + if (wallet) { + handleSetInstructions( + { governedAccount: governedAccount, getInstruction }, + index + ) + } + }, [form]) + + useEffect(() => { + setGovernedAccount(form.governedTokenAccount?.governance) + setMintInfo(form.governedTokenAccount?.extensions.mint?.account) + }, [form.governedTokenAccount]) + + const schema = getCastleWithdrawSchema() + + return ( + + { + handleSetForm({ value, propertyName: 'governedTokenAccount' }) + }} + value={form.governedTokenAccount} + error={formErrors['governedTokenAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + /> + + + + ) +} + +export default CastleWithdraw diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index 85eb39e5af..ad9d096ad5 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -61,6 +61,7 @@ import MakeAddSpotMarket from './components/instructions/Mango/MakeAddSpotMarket import MakeChangeSpotMarket from './components/instructions/Mango/MakeChangeSpotMarket' import MakeCreatePerpMarket from './components/instructions/Mango/MakeCreatePerpMarket' import useCreateProposal from '@hooks/useCreateProposal' +import CastleDeposit from './components/instructions/Castle/CastleDeposit' import MakeInitMarketParams from './components/instructions/Foresight/MakeInitMarketParams' import MakeInitMarketListParams from './components/instructions/Foresight/MakeInitMarketListParams' import MakeInitCategoryParams from './components/instructions/Foresight/MakeInitCategoryParams' @@ -70,6 +71,7 @@ import RealmConfig from './components/instructions/RealmConfig' import MakeAddMarketMetadataParams from './components/instructions/Foresight/MakeAddMarketMetadataParams' import CloseTokenAccount from './components/instructions/CloseTokenAccount' import { InstructionDataWithHoldUpTime } from 'actions/createProposal' +import CastleWithdraw from './components/instructions/Castle/CastleWithdraw' const schema = yup.object().shape({ title: yup.string().required('Title is required'), @@ -196,6 +198,7 @@ const New = () => { ) const instructions: UiInstruction[] = await handleGetInstructions() + let proposalAddress: PublicKey | null = null if (!realm) { handleTurnOffLoaders() @@ -305,6 +308,16 @@ const New = () => { return ( ) + case Instructions.Mint: + return + case Instructions.Base64: + return + case Instructions.None: + return + case Instructions.DepositIntoCastle: + return + case Instructions.WithrawFromCastle: + return case Instructions.DepositIntoVolt: return case Instructions.WithdrawFromVolt: @@ -331,10 +344,6 @@ const New = () => { governance={governance} /> ) - case Instructions.Mint: - return - case Instructions.Base64: - return case Instructions.CreateNftPluginRegistrar: return ( { governance={governance} > ) - case Instructions.None: - return case Instructions.MangoAddOracle: return ( diff --git a/utils/connection.ts b/utils/connection.ts index fae71729f0..cc85a160c3 100644 --- a/utils/connection.ts +++ b/utils/connection.ts @@ -35,3 +35,17 @@ export function getConnectionContext(cluster: string): ConnectionContext { endpoint: ENDPOINT!.url, } } + +/** + * Given ConnectionContext, find the network. + * @param connectionContext + * @returns EndpointType + */ +export function getNetworkFromEndpoint(endpoint: string) { + const network = ENDPOINTS.find((e) => e.url === endpoint) + if (!network) { + console.log(endpoint, ENDPOINTS) + throw new Error('Network not found') + } + return network?.name +} diff --git a/utils/instructionTools.ts b/utils/instructionTools.ts index 080565f517..7fa64a4f5e 100644 --- a/utils/instructionTools.ts +++ b/utils/instructionTools.ts @@ -11,7 +11,6 @@ import { } from '@solana/spl-token' import { SignerWalletAdapter, WalletAdapter } from '@solana/wallet-adapter-base' import { - Account, Keypair, PublicKey, SystemProgram, @@ -23,15 +22,11 @@ import { getMintNaturalAmountFromDecimal, parseMintNaturalAmountFromDecimal, } from '@tools/sdk/units' -import type { ConnectionContext } from 'utils/connection' +import { ConnectionContext } from 'utils/connection' import { getATA } from './ataTools' import { isFormValid } from './formValidation' import { getTokenAccountsByMint } from './tokens' import { UiInstruction } from './uiTypes/proposalCreationTypes' -import { ConnectedVoltSDK, FriktionSDK } from '@friktion-labs/friktion-sdk' -import { AnchorWallet } from '@friktion-labs/friktion-sdk/dist/cjs/src/miscUtils' -import { WSOL_MINT } from '@components/instructions/tools' -import Decimal from 'decimal.js' import { AssetAccount } from '@utils/uiTypes/assets' export const validateInstruction = async ({ @@ -44,176 +39,6 @@ export const validateInstruction = async ({ return isValid } -export async function getFriktionDepositInstruction({ - schema, - form, - amount, - connection, - wallet, - setFormErrors, -}: { - schema: any - form: any - amount: number - programId: PublicKey | undefined - connection: ConnectionContext - wallet: WalletAdapter | undefined - setFormErrors: any -}): Promise { - const isValid = await validateInstruction({ schema, form, setFormErrors }) - let serializedInstruction = '' - const prerequisiteInstructions: TransactionInstruction[] = [] - const governedTokenAccount = form.governedTokenAccount as AssetAccount - const voltVaultId = new PublicKey(form.voltVaultId as string) - - const signers: Keypair[] = [] - if ( - isValid && - amount && - governedTokenAccount?.extensions.token?.publicKey && - governedTokenAccount?.extensions.token && - governedTokenAccount?.extensions.mint?.account && - governedTokenAccount?.governance && - wallet - ) { - const sdk = new FriktionSDK({ - provider: { - connection: connection.current, - wallet: (wallet as unknown) as AnchorWallet, - }, - }) - const cVoltSDK = new ConnectedVoltSDK( - connection.current, - wallet.publicKey as PublicKey, - await sdk.loadVoltByKey(voltVaultId) - ) - - const voltVault = cVoltSDK.voltVault - const vaultMint = cVoltSDK.voltVault.vaultMint - - //we find true receiver address if its wallet and we need to create ATA the ata address will be the receiver - const { currentAddress: receiverAddress, needToCreateAta } = await getATA({ - connection: connection, - receiverAddress: governedTokenAccount.governance.pubkey, - mintPK: vaultMint, - wallet, - }) - //we push this createATA instruction to transactions to create right before creating proposal - //we don't want to create ata only when instruction is serialized - if (needToCreateAta) { - prerequisiteInstructions.push( - Token.createAssociatedTokenAccountInstruction( - ASSOCIATED_TOKEN_PROGRAM_ID, // always ASSOCIATED_TOKEN_PROGRAM_ID - TOKEN_PROGRAM_ID, // always TOKEN_PROGRAM_ID - vaultMint, // mint - receiverAddress, // ata - governedTokenAccount.governance.pubkey, // owner of token account - wallet.publicKey! // fee payer - ) - ) - } - - let pendingDepositInfo - try { - pendingDepositInfo = await cVoltSDK.getPendingDepositForUser() - } catch (err) { - pendingDepositInfo = null - } - - if ( - pendingDepositInfo && - pendingDepositInfo.roundNumber.lt(voltVault.roundNumber) && - pendingDepositInfo?.numUnderlyingDeposited?.gtn(0) - ) { - prerequisiteInstructions.push( - await cVoltSDK.claimPending(receiverAddress) - ) - } - - let depositTokenAccountKey: PublicKey | null - - if (governedTokenAccount.isSol) { - const { currentAddress: receiverAddress, needToCreateAta } = await getATA( - { - connection: connection, - receiverAddress: governedTokenAccount.governance.pubkey, - mintPK: new PublicKey(WSOL_MINT), - wallet, - } - ) - if (needToCreateAta) { - prerequisiteInstructions.push( - Token.createAssociatedTokenAccountInstruction( - ASSOCIATED_TOKEN_PROGRAM_ID, // always ASSOCIATED_TOKEN_PROGRAM_ID - TOKEN_PROGRAM_ID, // always TOKEN_PROGRAM_ID - new PublicKey(WSOL_MINT), // mint - receiverAddress, // ata - governedTokenAccount.governance.pubkey, // owner of token account - wallet.publicKey! // fee payer - ) - ) - } - depositTokenAccountKey = receiverAddress - } else { - depositTokenAccountKey = governedTokenAccount.extensions.transferAddress! - } - - try { - let decimals = 9 - - if (!governedTokenAccount.isSol) { - const underlyingAssetMintInfo = await new Token( - connection.current, - governedTokenAccount.extensions.mint!.publicKey, - TOKEN_PROGRAM_ID, - (null as unknown) as Account - ).getMintInfo() - decimals = underlyingAssetMintInfo.decimals - } - - const depositIx = governedTokenAccount.isSol - ? await cVoltSDK.depositWithTransfer( - new Decimal(amount), - depositTokenAccountKey, - receiverAddress, - governedTokenAccount.extensions.transferAddress!, - governedTokenAccount.governance.pubkey, - decimals - ) - : await cVoltSDK.deposit( - new Decimal(amount), - depositTokenAccountKey, - receiverAddress, - governedTokenAccount.governance.pubkey, - decimals - ) - - const governedAccountIndex = depositIx.keys.findIndex( - (k) => - k.pubkey.toString() === - governedTokenAccount.governance?.pubkey.toString() - ) - depositIx.keys[governedAccountIndex].isSigner = true - - serializedInstruction = serializeInstructionToBase64(depositIx) - } catch (e) { - if (e instanceof Error) { - throw new Error('Error: ' + e.message) - } - throw e - } - } - const obj: UiInstruction = { - serializedInstruction, - isValid, - governance: governedTokenAccount?.governance, - prerequisiteInstructions: prerequisiteInstructions, - signers, - shouldSplitIntoSeparateTxs: true, - } - return obj -} - export async function getGenericTransferInstruction({ schema, form, diff --git a/utils/instructions/Castle/index.ts b/utils/instructions/Castle/index.ts new file mode 100644 index 0000000000..552a8161c1 --- /dev/null +++ b/utils/instructions/Castle/index.ts @@ -0,0 +1,405 @@ +import { BN, WalletAdapter } from '@blockworks-foundation/mango-client' +import { + VaultConfig, + DeploymentEnvs, + Clusters, +} from '@castlefinance/vault-core' +import { VaultClient } from '@castlefinance/vault-sdk' +import { Provider } from '@castlefinance/vault-sdk/node_modules/@project-serum/anchor' +import { AnchorWallet } from '@friktion-labs/friktion-sdk/dist/cjs/src/miscUtils' +import { + serializeInstructionToBase64, + ProposalTransaction, + ProgramAccount, + WalletSigner, +} from '@solana/spl-governance' +import { + Token, + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, +} from '@solana/spl-token' +import { SignerWalletAdapter } from '@solana/wallet-adapter-base' +import { + PublicKey, + TransactionInstruction, + Keypair, + Connection, +} from '@solana/web3.js' +import { ConnectionContext, getNetworkFromEndpoint } from '@utils/connection' +import { validateInstruction } from '@utils/instructionTools' +import { AssetAccount } from '@utils/uiTypes/assets' +import { + CastleDepositForm, + CastleWithdrawForm, + UiInstruction, +} from '@utils/uiTypes/proposalCreationTypes' + +export async function getCastleDepositInstruction({ + schema, + form, + amount, + connection, + wallet, + setFormErrors, +}: { + schema: any + form: CastleDepositForm + amount: number + programId: PublicKey | undefined + connection: ConnectionContext + wallet: WalletAdapter | SignerWalletAdapter | undefined + setFormErrors: any +}): Promise { + const isValid = await validateInstruction({ schema, form, setFormErrors }) + + // NOTE - this should be `let serializedInstruction = ''` but it's const so the current changeset passes eslint + let serializedInstruction = '' + + const prerequisiteInstructions: TransactionInstruction[] = [] + const governedTokenAccount = form.governedTokenAccount as AssetAccount + + const signers: Keypair[] = [] + + if ( + isValid && + amount && + amount > 0 && + governedTokenAccount?.extensions.token?.publicKey && + governedTokenAccount?.extensions.token && + governedTokenAccount?.extensions.mint?.account && + governedTokenAccount?.governance && + wallet && + wallet.publicKey + ) { + const vaultClient = await getCastleVaultClientFromForm( + wallet, + connection, + form + ) + + const reserveTokenOwner = + governedTokenAccount.extensions.token.account.owner + + // Create the DAOs LP ATA if it does not exist already + let createLpAcctIx: TransactionInstruction | undefined = undefined + const userLpTokenAccount = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + vaultClient.getLpTokenMint(), + reserveTokenOwner, + true + ) + + try { + await vaultClient.getLpTokenAccountInfo(userLpTokenAccount) + } catch (error) { + console.log('Creating LP token account', error) + createLpAcctIx = Token.createAssociatedTokenAccountInstruction( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + vaultClient.getLpTokenMint(), + userLpTokenAccount, + reserveTokenOwner, + wallet.publicKey + ) + } + + // Get the deposit instruction + const { decimals } = governedTokenAccount.extensions.mint.account + const depositIx = vaultClient.getDepositIx( + new BN(amount * Math.pow(10, decimals)), + reserveTokenOwner, + userLpTokenAccount, + governedTokenAccount.pubkey + ) + + // Create the LP token account if necessary + if (createLpAcctIx) { + prerequisiteInstructions.push(createLpAcctIx) + } + + serializedInstruction = serializeInstructionToBase64(depositIx) + } + + // Build + return UI instruction + const obj: UiInstruction = { + serializedInstruction, + isValid, + governance: governedTokenAccount?.governance, + prerequisiteInstructions: prerequisiteInstructions, + signers, + shouldSplitIntoSeparateTxs: true, + } + console.log('cas: obj', obj) + + return obj +} + +export async function getCastleWithdrawInstruction({ + schema, + form, + amount, + connection, + wallet, + setFormErrors, +}: { + schema: any + form: CastleDepositForm + amount: number + programId: PublicKey | undefined + connection: ConnectionContext + wallet: WalletAdapter | SignerWalletAdapter | undefined + setFormErrors: any +}): Promise { + const isValid = await validateInstruction({ schema, form, setFormErrors }) + + // NOTE - this should be `let serializedInstruction = ''` but it's const so the current changeset passes eslint + let serializedInstruction = '' + + const prerequisiteInstructions: TransactionInstruction[] = [] + const governedTokenAccount = form.governedTokenAccount as AssetAccount + + const signers: Keypair[] = [] + + if ( + isValid && + amount && + amount > 0 && + governedTokenAccount?.extensions.token?.publicKey && + governedTokenAccount?.extensions.token && + governedTokenAccount?.extensions.mint?.account && + governedTokenAccount?.governance && + wallet && + wallet.publicKey + ) { + const vaultClient = await getCastleVaultClientFromForm( + wallet, + connection, + form + ) + + console.log(vaultClient) + + const lpTokenAccountOwner = + governedTokenAccount.extensions.token.account.owner + + // Create the DAOs Reserve ATA if it does not exist already + let createReserveAcctIx: TransactionInstruction | undefined = undefined + const userReserveTokenAccount = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + vaultClient.getReserveTokenMint(), + lpTokenAccountOwner, + true + ) + + try { + await vaultClient.getReserveTokenAccountInfo(userReserveTokenAccount) + } catch (error) { + console.log('Creating reserve token account', error) + createReserveAcctIx = Token.createAssociatedTokenAccountInstruction( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + vaultClient.getReserveTokenMint(), + userReserveTokenAccount, + lpTokenAccountOwner, + wallet.publicKey + ) + } + + // Get withdraw instruction. User selects the LP token to deposit back + // into the vault in exchange for the reserve token + const { decimals } = governedTokenAccount.extensions.mint.account + + const withdrawIx = vaultClient.getWithdrawIx( + new BN(amount * Math.pow(10, decimals)), + lpTokenAccountOwner, + governedTokenAccount.pubkey, + userReserveTokenAccount + ) + + // Create the reserve token account if necessary + if (createReserveAcctIx) { + prerequisiteInstructions.push(createReserveAcctIx) + } + + serializedInstruction = serializeInstructionToBase64(withdrawIx) + } + + // Build + return UI instruction + const obj: UiInstruction = { + serializedInstruction, + isValid, + governance: governedTokenAccount?.governance, + prerequisiteInstructions: prerequisiteInstructions, + signers, + shouldSplitIntoSeparateTxs: true, + } + console.log('cas: obj', obj) + + return obj +} + +/** + * Pulls the reconcile amount out of the proposal + * @param connection + * @param wallet + * @param proposalTx + */ +export async function getCastleReconcileInstruction( + connection: Connection, + wallet: WalletSigner, + instruction: ProgramAccount +) { + const vaultClient = await getCastleVaultClientFromProposal( + wallet, + connection, + instruction + ) + + // Bundle reconcile and refresh into the same tx + const ix = instruction.account.getSingleInstruction() + + // Grab the amount parameter from the instruction :^) + const amount = new BN( + [...ix.data.slice(8, 16)] + .reverse() + .map((i) => `00${i.toString(16)}`.slice(-2)) + .join(''), + 16 + ).toNumber() + + console.log('Grabbed', amount, 'from proposal instruction') + + return await vaultClient.getReconcileTxs(amount) +} + +/** + * Constructs refresh transaction based on network and vault and mint and strategy + * @param connection + * @param wallet + * @param instructionOption + * @returns Refresh transaction for the specified mint vault + */ +export async function getCastleRefreshInstruction( + connection: Connection, + wallet: any, + instruction: ProgramAccount +) { + const vaultClient = await getCastleVaultClientFromProposal( + wallet, + connection, + instruction + ) + + const refreshIx = vaultClient.getRefreshIx() + + return refreshIx +} + +/** + * Get the vault that matches the current network and + * pulls the vaultId from a form + * @param network + * @param instruction + * @returns + */ +const getCastleVaultClientFromForm = async ( + wallet: WalletSigner, + connection: ConnectionContext, + form: CastleDepositForm | CastleWithdrawForm +) => { + // Create a new provider + const provider = new Provider( + connection.current, + (wallet as unknown) as AnchorWallet, + { + preflightCommitment: 'confirmed', + commitment: 'confirmed', + } + ) + + const vaults = (await getCastleVaults()).filter((v) => + connection.cluster == 'mainnet' + ? v.cluster == Clusters.mainnetBeta + : v.cluster == Clusters.devnet + ) + + // Getting the vault from a user-inputted form + const vault = vaults.find((v) => v.vault_id === form.castleVaultId) + + if (!vault) { + throw new Error('Vault not found in config') + } + + // Load the vault + const vaultClient = await VaultClient.load( + provider, + new PublicKey(vault.vault_id), + connection.cluster == 'mainnet' + ? DeploymentEnvs.mainnet + : DeploymentEnvs.devnetStaging + ) + + return vaultClient +} + +/** + * Get the vault that matches the current network and + * pulls the vaultId from an instruction proposal + * @param network + * @param instruction + * @returns + */ +const getCastleVaultClientFromProposal = async ( + wallet: WalletSigner, + connection: Connection, + instruction: ProgramAccount +) => { + // Create a new provider + const provider = new Provider( + connection, + (wallet as unknown) as AnchorWallet, + { + preflightCommitment: 'confirmed', + commitment: 'confirmed', + } + ) + + const network = getNetworkFromEndpoint(connection.rpcEndpoint) + const vaults = await getCastleVaults() + + // Getting the vault from a proposal instruction + const vault = vaults + .filter((v) => + network == 'mainnet' + ? v.cluster == Clusters.mainnetBeta + : v.cluster == Clusters.devnet + ) + .find((v) => + instruction.account.instructions + .map((i) => i.accounts.map((a) => a.pubkey.toBase58())) + .flat() + .includes(v.vault_id) + ) + + if (!vault) { + throw new Error('Vault not found in config') + } + + // Load the vault + const vaultClient = await VaultClient.load( + provider, + new PublicKey(vault.vault_id), + network == 'mainnet' ? DeploymentEnvs.mainnet : DeploymentEnvs.devnetStaging + ) + + return vaultClient +} + +// Get the vaults from the config api +export const getCastleVaults = async () => { + const configResponse = await fetch('https://api.castle.finance/configs') + const vaults = (await configResponse.json()) as VaultConfig[] + return vaults +} diff --git a/utils/send.tsx b/utils/send.tsx index 51ba3482dc..7eb075aed2 100644 --- a/utils/send.tsx +++ b/utils/send.tsx @@ -103,9 +103,17 @@ export async function signTransactions({ transaction.partialSign(...signers) } }) - return await wallet.signAllTransactions( - transactionsAndSigners.map(({ transaction }) => transaction) - ) + + let signed + try { + signed = await wallet.signAllTransactions( + transactionsAndSigners.map(({ transaction }) => transaction) + ) + } catch (e) { + console.log(e) + } + + return signed } export async function sendSignedTransaction({ @@ -169,6 +177,7 @@ export async function sendSignedTransaction({ console.log('sined transaction', signedTransaction) + // Simulate failed transaction to parse out an error reason try { console.log('start simulate') simulateResult = ( @@ -180,6 +189,7 @@ export async function sendSignedTransaction({ console.log('simulate result', simulateResult) + // Parse and throw error if simulation fails if (simulateResult && simulateResult.err) { if (simulateResult.logs) { console.log('simulate resultlogs', simulateResult.logs) @@ -211,6 +221,123 @@ export async function sendSignedTransaction({ return txid } +/** + * Send a primary transaction and an adjacent one + */ +export async function sendSignedAndAdjacentTransactions({ + signedTransaction, + adjacentTransaction, + connection, + sendingMessage = 'Sending transaction...', + successMessage = 'Transaction confirmed', + timeout = DEFAULT_TIMEOUT, +}: { + signedTransaction: Transaction + adjacentTransaction: Transaction + connection: Connection + sendingMessage?: string + successMessage?: string + timeout?: number +}): Promise { + notify({ message: sendingMessage }) + + // Serialize both transactions + const rawTransaction = signedTransaction.serialize() + const rawAdjTransaction = adjacentTransaction.serialize() + + const proposalTxPromise = connection.sendRawTransaction(rawAdjTransaction, { + skipPreflight: true, + }) + await sleep(30) + const adjTxPromise = connection.sendRawTransaction(rawTransaction, { + skipPreflight: true, + }) + + const [proposalTxId, adjTxId] = await Promise.all([ + proposalTxPromise, + adjTxPromise, + ]) + + // Retry mechanism + let done = false + const startTime = getUnixTs() + console.log('Started awaiting confirmation for', proposalTxId) + ;(async () => { + while (!done && getUnixTs() - startTime < timeout) { + console.log('RETRYING') + connection.sendRawTransaction(rawTransaction, { + skipPreflight: true, + }) + await sleep(3000) + } + })() + + try { + console.log( + 'calling signatures confirmation', + await awaitTransactionSignatureConfirmation(adjTxId, timeout, connection), + await awaitTransactionSignatureConfirmation( + proposalTxId, + timeout, + connection + ) + ) + } catch (err) { + if (err.timeout) { + throw new Error('Timed out awaiting confirmation on transaction') + } + + let simulateResult: SimulatedTransactionResponse | null = null + + console.log('signed transaction', signedTransaction) + + // Simulate failed transaction to parse out an error reason + try { + console.log('start simulate') + simulateResult = ( + await simulateTransaction(connection, signedTransaction, 'single') + ).value + } catch (error) { + console.log('Error simulating: ', error) + } + + console.log('simulate result', simulateResult) + + // Parse and throw error if simulation fails + if (simulateResult && simulateResult.err) { + if (simulateResult.logs) { + console.log('simulate resultlogs', simulateResult.logs) + + for (let i = simulateResult.logs.length - 1; i >= 0; --i) { + const line = simulateResult.logs[i] + + if (line.startsWith('Program log: ')) { + throw new TransactionError( + 'Transaction failed: ' + line.slice('Program log: '.length), + proposalTxId + ) + } + } + } + throw new TransactionError( + JSON.stringify(simulateResult.err), + proposalTxId + ) + } + + console.log('transaction error') + + throw new TransactionError('Transaction failed', proposalTxId) + } finally { + done = true + } + + notify({ message: successMessage, type: 'success', txid: proposalTxId }) + + console.log('Latency', proposalTxId, getUnixTs() - startTime) + return proposalTxId +} + async function awaitTransactionSignatureConfirmation( txid: TransactionSignature, timeout: number, diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 751a323f8d..a5b842b56c 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -30,6 +30,22 @@ export interface SplTokenTransferForm { mintInfo: MintInfo | undefined } +export interface CastleDepositForm { + amount: number | undefined + governedTokenAccount: AssetAccount | undefined + castleVaultId: string + programId: string | undefined + mintInfo: MintInfo | undefined +} + +export interface CastleWithdrawForm { + amount: number | undefined + governedTokenAccount: AssetAccount | undefined + castleVaultId: string + programId: string | undefined + mintInfo: MintInfo | undefined +} + export interface FriktionDepositForm { amount: number | undefined governedTokenAccount: AssetAccount | undefined @@ -274,6 +290,8 @@ export enum Instructions { Grant, Clawback, CreateAssociatedTokenAccount, + DepositIntoCastle, + WithrawFromCastle, DepositIntoVolt, WithdrawFromVolt, CreateSolendObligationAccount, diff --git a/utils/validations.tsx b/utils/validations.tsx index 931b131a6a..271f78ca9f 100644 --- a/utils/validations.tsx +++ b/utils/validations.tsx @@ -233,6 +233,59 @@ export const getFriktionDepositSchema = ({ form }) => { }) } +export const getCastleDepositSchema = ({ form }) => { + const governedTokenAccount = form.governedTokenAccount as AssetAccount + return yup.object().shape({ + governedTokenAccount: yup.object().required('Source account is required'), + amount: yup + .number() + .typeError('Amount is required') + .test( + 'amount', + 'Transfer amount must be less than the source account available amount', + async function (val: number) { + const isNft = governedTokenAccount?.isNft + if (isNft) { + return true + } + if (val && !form.governedTokenAccount) { + return this.createError({ + message: `Please select source account to validate the amount`, + }) + } + if ( + val && + governedTokenAccount && + governedTokenAccount?.extensions.mint + ) { + const mintValue = getMintNaturalAmountFromDecimalAsBN( + val, + governedTokenAccount?.extensions.mint.account.decimals + ) + return !!(governedTokenAccount?.extensions.token?.publicKey && + !governedTokenAccount.isSol + ? governedTokenAccount.extensions.token.account.amount.gte( + mintValue + ) + : new BN( + governedTokenAccount.extensions.solAccount!.lamports + ).gte(mintValue)) + } + return this.createError({ + message: `Amount is required`, + }) + } + ), + }) +} + +export const getCastleWithdrawSchema = () => { + return yup.object().shape({ + governedTokenAccount: yup.object().required('Source account is required'), + amount: yup.number().typeError('Amount is required'), + }) +} + export const getFriktionWithdrawSchema = () => { return yup.object().shape({ governedTokenAccount: yup.object().required('Source account is required'), diff --git a/yarn.lock b/yarn.lock index 7365c78d71..f1ca2b212b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -620,6 +620,27 @@ "@solana/spl-token" "^0.1.8" typescript "^4.5.4" +"@castlefinance/vault-core@^0.1.0", "@castlefinance/vault-core@^0.1.3": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@castlefinance/vault-core/-/vault-core-0.1.5.tgz#984da02fc4ca1270359c9c3283243658905eb562" + integrity sha512-Evq4CslcF5wsnMpd0oTGq4iNfjqprut25GQTQFI6mCTITFtrCa7G7cprw9jaBSXlyX/fpa3LI799VtPhrCoViA== + +"@castlefinance/vault-sdk@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@castlefinance/vault-sdk/-/vault-sdk-2.1.2.tgz#7006fb3c1807be2ef3db0599cf154da0513f15f2" + integrity sha512-EIKsJm8jUJqcflE2la1jvh/rkOfD3vVI07uMW9yEJ0PmzKUX7QX6PGXnENmnfqzy5NjZ5a6gTw7gOrhRuK4Ofg== + dependencies: + "@castlefinance/vault-core" "^0.1.0" + "@jet-lab/jet-engine" "^0.2.15" + "@port.finance/port-sdk" "^0.2.33" + "@project-serum/anchor" "0.18.2" + "@solana/spl-token" "^0.1.8" + "@solana/web3.js" "^1.31.0" + "@solendprotocol/solend-sdk" "^0.4.4" + big.js "^6.1.1" + bigint-buffer "^1.1.5" + buffer-layout "^1.2.2" + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -899,6 +920,18 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== +"@hapi/hoek@^9.0.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@headlessui/react@^1.2.0", "@headlessui/react@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.5.0.tgz#483b44ba2c8b8d4391e1d2c863898d7dd0cc0296" @@ -1121,6 +1154,24 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jet-lab/jet-engine@^0.2.15": + version "0.2.50" + resolved "https://registry.yarnpkg.com/@jet-lab/jet-engine/-/jet-engine-0.2.50.tgz#55dc001018a0eb012940e21563a2c40582352eeb" + integrity sha512-N73kuVmJiPoQUTAVTHX88qRVQp2w3YBhHWj089AYCx/Ao8xxHea5LggZVP/NYM+dji/13BKeCeoUoUo2S9tUGA== + dependencies: + "@project-serum/anchor" "^0.23.0" + "@project-serum/serum" "^0.13.60" + "@pythnetwork/client" "^2.5.1" + "@solana/buffer-layout" "^4.0.0" + "@solana/buffer-layout-utils" "^0.2.0" + "@solana/spl-governance" "^0.0.29" + "@solana/spl-token" "^0.2.0" + "@solana/web3.js" "^1.36.0" + eventemitter3 "^4.0.7" + lodash "^4.17.21" + react "^17.0.0" + react-dom "^17.0.0" + "@ledgerhq/devices@^6.20.0": version "6.20.0" resolved "https://registry.npmjs.org/@ledgerhq/devices/-/devices-6.20.0.tgz" @@ -1712,11 +1763,50 @@ resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.9.3.tgz" integrity sha512-xDu17cEfh7Kid/d95kB6tZsLOmSWKCZKtprnhVepjsSaCij+lM3mItSJDuuHDMbCWTh8Ejmebwb+KONcCJ0eXQ== +"@port.finance/port-sdk@^0.2.33": + version "0.2.67" + resolved "https://registry.yarnpkg.com/@port.finance/port-sdk/-/port-sdk-0.2.67.tgz#48ddd27af7b14c956fa6a2941d84edc61eb9c082" + integrity sha512-qm7IHdfP/vhSv41RHveEUsJSBOknPeWYuRwZS5oISwe1kvcOBHkuh/9GuJD24TlE9HaoO+JGq3rpn8YLFjNh8w== + dependencies: + "@project-serum/anchor" "^0.21.0" + "@saberhq/solana-contrib" "^1.12.53" + "@saberhq/token-utils" "^1.12.53" + "@solana/buffer-layout" "^3.0.0" + "@solana/spl-token" "^0.1.8" + "@solana/spl-token-registry" "^0.2.1107" + big.js "^6.1.1" + bignumber.js "^9.0.1" + buffer-layout "1.2.2" + jsbi "^4.1.0" + prettier "^2.4.1" + tiny-invariant "^1.2.0" + typescript "^4.3.5" + "@project-serum/anchor-cli@^0.18.2": version "0.18.2" resolved "https://registry.npmjs.org/@project-serum/anchor-cli/-/anchor-cli-0.18.2.tgz" integrity sha512-zZvLSa0DvsjsKSK8cU7ENqPB1tQ8WCT6dTY2IaJjWdG0UvXUTri8phnYyF5+Zti+frTdKyggOtI+i1QApOWhRQ== +"@project-serum/anchor@0.18.2", "@project-serum/anchor@^0.18.0", "@project-serum/anchor@^0.18.2": + version "0.18.2" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.18.2.tgz#0f13b5c2046446b7c24cf28763eec90febb28485" + integrity sha512-uyjiN/3Ipp+4hrZRm/hG18HzGLZyvP790LXrCsGO3IWxSl28YRhiGEpKnZycfMW94R7nxdUoE3wY67V+ZHSQBQ== + dependencies: + "@project-serum/borsh" "^0.2.2" + "@solana/web3.js" "^1.17.0" + base64-js "^1.5.1" + bn.js "^5.1.2" + bs58 "^4.0.1" + buffer-layout "^1.2.0" + camelcase "^5.3.1" + crypto-hash "^1.3.0" + eventemitter3 "^4.0.7" + find "^0.3.0" + js-sha256 "^0.9.0" + pako "^2.0.3" + snake-case "^3.0.4" + toml "^3.0.0" + "@project-serum/anchor@0.20.1", "@project-serum/anchor@^0.20.1": version "0.20.1" resolved "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.20.1.tgz" @@ -1818,26 +1908,6 @@ snake-case "^3.0.4" toml "^3.0.0" -"@project-serum/anchor@^0.18.0", "@project-serum/anchor@^0.18.2": - version "0.18.2" - resolved "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.18.2.tgz" - integrity sha512-uyjiN/3Ipp+4hrZRm/hG18HzGLZyvP790LXrCsGO3IWxSl28YRhiGEpKnZycfMW94R7nxdUoE3wY67V+ZHSQBQ== - dependencies: - "@project-serum/borsh" "^0.2.2" - "@solana/web3.js" "^1.17.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.0" - camelcase "^5.3.1" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - find "^0.3.0" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - toml "^3.0.0" - "@project-serum/anchor@^0.21.0": version "0.21.0" resolved "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.21.0.tgz" @@ -1919,6 +1989,17 @@ bn.js "^5.1.2" buffer-layout "^1.2.0" +"@project-serum/serum@^0.13.60": + version "0.13.65" + resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.65.tgz#6d3cf07912f13985765237f053cca716fe84b0b0" + integrity sha512-BHRqsTqPSfFB5p+MgI2pjvMBAQtO8ibTK2fYY96boIFkCI3TTwXDt2gUmspeChKO2pqHr5aKevmexzAcXxrSRA== + dependencies: + "@project-serum/anchor" "^0.11.1" + "@solana/spl-token" "^0.1.6" + "@solana/web3.js" "^1.21.0" + bn.js "^5.1.2" + buffer-layout "^1.2.0" + "@project-serum/sol-wallet-adapter@0.2.0": version "0.2.0" resolved "https://registry.npmjs.org/@project-serum/sol-wallet-adapter/-/sol-wallet-adapter-0.2.0.tgz" @@ -2156,6 +2237,19 @@ tiny-invariant "^1.2.0" tslib "^2.3.1" +"@saberhq/solana-contrib@^1.12.53", "@saberhq/solana-contrib@^1.13.0": + version "1.13.0" + resolved "https://registry.yarnpkg.com/@saberhq/solana-contrib/-/solana-contrib-1.13.0.tgz#0e5f360cfbed90e9786d0e89db6589f8c5299bab" + integrity sha512-LNFd1KHQ9GrbeAyF61moTtIt92qC+9bRaP1nHMNyOxjp7Q0sQhBm8QrW8Nez5tz8H3OYB2ZFr7LWvQ+6ddn9fg== + dependencies: + "@solana/buffer-layout" "^4.0.0" + "@types/promise-retry" "^1.1.3" + "@types/retry" "^0.12.2" + promise-retry "^2.0.1" + retry "^0.13.1" + tiny-invariant "^1.2.0" + tslib "^2.4.0" + "@saberhq/token-utils@^1.11.3", "@saberhq/token-utils@^1.12.48": version "1.12.48" resolved "https://registry.npmjs.org/@saberhq/token-utils/-/token-utils-1.12.48.tgz" @@ -2168,6 +2262,18 @@ tiny-invariant "^1.2.0" tslib "^2.3.1" +"@saberhq/token-utils@^1.12.53": + version "1.13.0" + resolved "https://registry.yarnpkg.com/@saberhq/token-utils/-/token-utils-1.13.0.tgz#ffafb6a3a38742761a44a831c2f8d70875d6d8a8" + integrity sha512-24GxCywSRqn37Nx8tZ6w9pFjg+/RPUVbxh6pEouSBvB/H5eE3kqt5uitwIpRTIctkNcH+1x0khnEn1evk99HPg== + dependencies: + "@saberhq/solana-contrib" "^1.13.0" + "@solana/buffer-layout" "^4.0.0" + "@solana/spl-token" "^0.1.8" + "@ubeswap/token-math" "^4.4.8" + tiny-invariant "^1.2.0" + tslib "^2.4.0" + "@saberhq/use-solana@^1.12.26": version "1.12.35" resolved "https://registry.npmjs.org/@saberhq/use-solana/-/use-solana-1.12.35.tgz" @@ -2326,6 +2432,23 @@ dependencies: "@sentry/cli" "^1.73.0" +"@sideway/address@^4.1.3": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" + integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" + integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" @@ -2339,6 +2462,16 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@solana/buffer-layout-utils@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz#b45a6cab3293a2eb7597cceb474f229889d875ca" + integrity sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g== + dependencies: + "@solana/buffer-layout" "^4.0.0" + "@solana/web3.js" "^1.32.0" + bigint-buffer "^1.1.5" + bignumber.js "^9.0.1" + "@solana/buffer-layout@^3.0.0": version "3.0.0" resolved "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-3.0.0.tgz" @@ -2361,6 +2494,18 @@ "@project-serum/anchor" "^0.24.2" "@project-serum/serum" "^0.13.61" +"@solana/spl-governance@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@solana/spl-governance/-/spl-governance-0.0.29.tgz#8a691ace5242a1174ede73657c06267184f1c4b3" + integrity sha512-+Wrk4pHKDDzw7d2gXtpplslfLwIjWIDLB4o78LSiiPCtY+fObtKabWwyH/AVWXhebSGj+bkW6j0i9/WE4xbnCg== + dependencies: + "@solana/web3.js" "^1.22.0" + bignumber.js "^9.0.1" + bn.js "^5.1.3" + borsh "^0.3.1" + bs58 "^4.0.1" + superstruct "^0.15.2" + "@solana/spl-governance@^0.0.34": version "0.0.34" resolved "https://registry.yarnpkg.com/@solana/spl-governance/-/spl-governance-0.0.34.tgz#c61d81d356dbcee961bbc85e5d3538846fea57ad" @@ -2392,6 +2537,13 @@ tweetnacl "^1.0.3" webpack-dev-server "^3.11.2" +"@solana/spl-token-registry@^0.2.1107": + version "0.2.3797" + resolved "https://registry.yarnpkg.com/@solana/spl-token-registry/-/spl-token-registry-0.2.3797.tgz#48e5ca97c2dc064724ac4997aeb96abb2278a28c" + integrity sha512-NrRVdM66Kvjef4bYQc9ynJvJtNW/JIWu7PvtuwqwR9R5Omzr5Cm5JWyM5NQDcDO/L8F8xJ8BYXA7PUKu0AkUxA== + dependencies: + cross-fetch "3.0.6" + "@solana/spl-token-registry@^0.2.3470": version "0.2.3636" resolved "https://registry.yarnpkg.com/@solana/spl-token-registry/-/spl-token-registry-0.2.3636.tgz#cb5b8eb4317735f71df301e198be821ddcb982b7" @@ -2423,6 +2575,16 @@ buffer-layout "^1.2.0" dotenv "10.0.0" +"@solana/spl-token@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.2.0.tgz#329bb6babb5de0f9c40035ddb1657f01a8347acd" + integrity sha512-RWcn31OXtdqIxmkzQfB2R+WpsJOVS6rKuvpxJFjvik2LyODd+WN58ZP3Rpjpro03fscGAkzlFuP3r42doRJgyQ== + dependencies: + "@solana/buffer-layout" "^4.0.0" + "@solana/buffer-layout-utils" "^0.2.0" + "@solana/web3.js" "^1.32.0" + start-server-and-test "^1.14.0" + "@solana/wallet-adapter-base@^0.9.1", "@solana/wallet-adapter-base@^0.9.2": version "0.9.2" resolved "https://registry.npmjs.org/@solana/wallet-adapter-base/-/wallet-adapter-base-0.9.2.tgz" @@ -2594,6 +2756,45 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" +"@solana/web3.js@^1.32.0": + version "1.41.4" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.41.4.tgz#595aa29a4a61c181b8c8f5cf0bbef80b4739cfab" + integrity sha512-2/mjqUcGsBkLEvKxA+rWFE1vODBycAMa62r3wm3Uzb6nmsXQQNTotB+6YoeLQmF0mxVoy8ZA+/QENzBkGkPzSg== + dependencies: + "@babel/runtime" "^7.12.5" + "@ethersproject/sha2" "^5.5.0" + "@solana/buffer-layout" "^4.0.0" + "@solana/buffer-layout-utils" "^0.2.0" + bn.js "^5.0.0" + borsh "^0.7.0" + bs58 "^4.0.1" + buffer "6.0.1" + cross-fetch "^3.1.4" + fast-stable-stringify "^1.0.0" + jayson "^3.4.4" + js-sha3 "^0.8.0" + rpc-websockets "^7.4.2" + secp256k1 "^4.0.2" + sinon-chai "^3.7.0" + superstruct "^0.14.2" + tweetnacl "^1.0.0" + +"@solendprotocol/solend-sdk@^0.4.4": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@solendprotocol/solend-sdk/-/solend-sdk-0.4.9.tgz#c0e3f24148c4951bf0cfdf16c884bc4ffbb42b7b" + integrity sha512-tVsvE0kap58/eMeeSonv0tEiqlnvYC0x90MXKoGz2D0oyQBx9Eneba5wXDh9FJ3S5S2RFqwXIAnmdOzTLZzV/A== + dependencies: + "@pythnetwork/client" "^2.5.1" + "@solana/buffer-layout" "^3.0.0" + "@solana/spl-token" "^0.1.8" + "@solana/web3.js" "^1.31.0" + axios "^0.24.0" + bignumber.js "^9.0.2" + bn.js "^5.2.0" + buffer "^6.0.3" + buffer-layout "^1.2.0" + isomorphic-fetch "^3.0.0" + "@solendprotocol/solend-sdk@^0.5.5": version "0.5.5" resolved "https://registry.yarnpkg.com/@solendprotocol/solend-sdk/-/solend-sdk-0.5.5.tgz#a8077532e1ca2ecd7d619fee4f339cd6b33e4a04" @@ -2994,6 +3195,11 @@ resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz" integrity sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g== +"@types/retry@^0.12.2": + version "0.12.2" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" + integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== + "@types/scheduler@*": version "0.16.1" resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz" @@ -3163,6 +3369,17 @@ toformat "^2.0.0" tslib "^2.3.1" +"@ubeswap/token-math@^4.4.8": + version "4.4.8" + resolved "https://registry.yarnpkg.com/@ubeswap/token-math/-/token-math-4.4.8.tgz#7530627c6d11ecfb3a8159b66567764256352f9e" + integrity sha512-/j8fld29BlWUA3hfBgFPuVorzOkhvnUyORk053pPJX0m9zhoFieRIT65A2sOGgF5Cffxj/OjN1ycAatD8vv6xg== + dependencies: + big.js "^6.1.1" + decimal.js-light "^2.5.1" + tiny-invariant "^1.2.0" + toformat "^2.0.0" + tslib "^2.4.0" + JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" @@ -3600,6 +3817,13 @@ aws4@^1.8.0: resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axios@^0.21.1: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + axios@^0.24.0: version "0.24.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" @@ -3740,6 +3964,13 @@ big.js@^6.1.1: resolved "https://registry.npmjs.org/big.js/-/big.js-6.1.1.tgz" integrity sha512-1vObw81a8ylZO5ePrtMay0n018TcftpTA5HFKDaSuiUDBo8biRBtjIobw60OpwuvrGk+FsxKamqN4cnmj/eXdg== +bigint-buffer@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" + integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA== + dependencies: + bindings "^1.3.0" + bignumber.js@^9.0.1, bignumber.js@^9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.2.tgz#71c6c6bed38de64e24a65ebe16cfcf23ae693673" @@ -3787,6 +4018,11 @@ bip32@^2.0.6: typeforce "^1.11.5" wif "^2.0.6" +bluebird@3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + bn.js@^4.11.8, bn.js@^4.11.9: version "4.12.0" resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" @@ -3955,7 +4191,7 @@ buffer-indexof@^1.0.0: resolved "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz" integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== -buffer-layout@^1.2.0, buffer-layout@^1.2.1, buffer-layout@^1.2.2: +buffer-layout@1.2.2, buffer-layout@^1.2.0, buffer-layout@^1.2.1, buffer-layout@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz" integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA== @@ -4181,6 +4417,11 @@ check-error@^1.0.2: resolved "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz" integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= +check-more-types@2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= + chokidar@^2.1.8: version "2.1.8" resolved "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz" @@ -4858,10 +5099,10 @@ dataloader@^1.4.0: resolved "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz" integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw== -dayjs@^1.10.7: - version "1.10.7" - resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz" - integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== +dayjs@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.1.tgz#90b33a3dda3417258d48ad2771b415def6545eb0" + integrity sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA== debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" @@ -4876,6 +5117,13 @@ debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: dependencies: ms "2.1.2" +debug@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + dependencies: + ms "2.1.2" + debug@^3.1.1, debug@^3.2.6: version "3.2.7" resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" @@ -5187,9 +5435,9 @@ dset@^2.0.1: version "2.1.0" resolved "https://registry.npmjs.org/dset/-/dset-2.1.0.tgz" -duplexer@^0.1.2: +duplexer@^0.1.2, duplexer@~0.1.1: version "0.1.2" - resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== ecc-jsbn@~0.1.1: @@ -5545,6 +5793,19 @@ etag@~1.8.1: resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + eventemitter3@^4.0.0, eventemitter3@^4.0.4, eventemitter3@^4.0.7: version "4.0.7" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" @@ -5562,6 +5823,21 @@ eventsource@^1.0.7: dependencies: original "^1.0.0" +execa@5.1.1, execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + execa@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz" @@ -5589,21 +5865,6 @@ execa@^4.1.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - exit@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" @@ -5762,6 +6023,11 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" +fast-stable-stringify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313" + integrity sha1-XFVDRisiru79NtBbNOUceMuG0xM= + fastest-levenshtein@^1.0.12: version "1.0.12" resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz" @@ -5893,6 +6159,11 @@ follow-redirects@^1.0.0, follow-redirects@^1.14.4, follow-redirects@^1.14.7, fol resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== +follow-redirects@^1.14.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.0.tgz#06441868281c86d0dda4ad8bdaead2d02dca89d4" + integrity sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" @@ -5957,6 +6228,11 @@ fresh@0.5.2: resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= + fs-extra@^10.0.0: version "10.0.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz" @@ -7653,6 +7929,17 @@ jest@^27.4.5: import-local "^3.0.2" jest-cli "^27.4.5" +joi@^17.4.0: + version "17.6.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2" + integrity sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" + "@sideway/formula" "^3.0.0" + "@sideway/pinpoint" "^2.0.0" + js-sha256@^0.9.0: version "0.9.0" resolved "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz" @@ -7865,6 +8152,11 @@ kleur@^4.0.3: resolved "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz" integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA== +lazy-ass@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= + leven@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" @@ -8274,6 +8566,11 @@ map-cache@^0.2.2: resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz" @@ -9810,6 +10107,13 @@ pathval@^1.1.1: resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= + dependencies: + through "~2.3" + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" @@ -10236,6 +10540,11 @@ prettier@^2.0.2: version "2.2.1" resolved "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz" +prettier@^2.4.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" + integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== + prettier@^2.5.1: version "2.5.1" resolved "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz" @@ -10379,6 +10688,13 @@ prr@~1.0.1: resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= +ps-tree@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" + integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== + dependencies: + event-stream "=3.3.4" + psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz" @@ -10553,6 +10869,15 @@ react-device-detect@^2.1.2: dependencies: ua-parser-js "^0.7.30" +react-dom@^17.0.0: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" + integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler "^0.20.2" + react-dom@^18.0.0: version "18.0.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.0.0.tgz#26b88534f8f1dbb80853e1eabe752f24100d8023" @@ -10668,7 +10993,7 @@ react-use-gesture@^9.1.3: resolved "https://registry.npmjs.org/react-use-gesture/-/react-use-gesture-9.1.3.tgz" integrity sha512-CdqA2SmS/fj3kkS2W8ZU8wjTbVBAIwDWaRprX7OKaj7HlGwBasGEFggmk5qNklknqk9zK/h8D355bEJFTpqEMg== -react@^17.0.2: +react@^17.0.0, react@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== @@ -11064,6 +11389,13 @@ rxjs@6, rxjs@^6.6.7: dependencies: tslib "^1.9.0" +rxjs@^7.1.0: + version "7.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" + integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== + dependencies: + tslib "^2.1.0" + sade@^1.7.3: version "1.8.1" resolved "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz" @@ -11098,6 +11430,14 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" +scheduler@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler@^0.21.0: version "0.21.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820" @@ -11306,6 +11646,11 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" +sinon-chai@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-3.7.0.tgz#cfb7dec1c50990ed18c153f1840721cf13139783" + integrity sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g== + sirv@^1.0.7: version "1.0.19" resolved "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz" @@ -11531,6 +11876,13 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= + dependencies: + through "2" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" @@ -11564,6 +11916,19 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +start-server-and-test@^1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.14.0.tgz#c57f04f73eac15dd51733b551d775b40837fdde3" + integrity sha512-on5ELuxO2K0t8EmNj9MtVlFqwBMxfWOhu4U7uZD1xccVpFlOQKR93CSe0u98iQzfNxRyaNTb/CdadbNllplTsw== + dependencies: + bluebird "3.7.2" + check-more-types "2.24.0" + debug "4.3.2" + execa "5.1.1" + lazy-ass "1.6.0" + ps-tree "1.2.0" + wait-on "6.0.0" + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz" @@ -11577,6 +11942,13 @@ static-extend@^0.1.1: resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= + dependencies: + duplexer "~0.1.1" + string-argv@0.3.1: version "0.3.1" resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz" @@ -11960,7 +12332,7 @@ throat@^6.0.1: resolved "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz" integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== -"through@>=2.2.7 <3", through@^2.3.8: +through@2, "through@>=2.2.7 <3", through@^2.3.8, through@~2.3, through@~2.3.1: version "2.3.8" resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -12199,6 +12571,11 @@ tslib@^2.0.0, tslib@^2.0.3, tslib@^2.3.0, tslib@^2.3.1: resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@^2.1.0, tslib@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" @@ -12689,6 +13066,17 @@ wait-for-event@^2.0.1: resolved "https://registry.npmjs.org/wait-for-event/-/wait-for-event-2.0.1.tgz" integrity sha512-CuUBvs/TnLfmgJvCVTXDU8yl3DyTilEzMYUe7mlGWtiQU1gndKT/lvdaUatG4AVwq+3heXugYTILZDOm4etc2Q== +wait-on@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.0.tgz#7e9bf8e3d7fe2daecbb7a570ac8ca41e9311c7e7" + integrity sha512-tnUJr9p5r+bEYXPUdRseolmz5XqJTTj98JgOsfBn7Oz2dxfE2g3zw1jE+Mo8lopM3j3et/Mq1yW7kKX6qw7RVw== + dependencies: + axios "^0.21.1" + joi "^17.4.0" + lodash "^4.17.21" + minimist "^1.2.5" + rxjs "^7.1.0" + walk-up-path@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz"