From 48de59e9168a6f6c9187b1c572dddeedbd400668 Mon Sep 17 00:00:00 2001 From: exromany Date: Mon, 16 Aug 2021 12:12:54 +0300 Subject: [PATCH 01/10] move models/metaplex to common pkg --- js/packages/common/src/actions/auction.ts | 2 +- js/packages/common/src/contexts/index.tsx | 1 - js/packages/common/src/models/index.ts | 1 + .../src/models/metaplex/claimBid.ts | 8 +--- .../metaplex/decommissionAuctionManager.ts | 2 +- .../deprecatedInitAuctionManagerV1.ts | 2 +- ...tedPopulateParticipationPrintingAccount.ts | 16 +++---- .../deprecatedRedeemParticipationBid.ts | 2 +- .../src/models/metaplex/deprecatedStates.ts | 2 +- .../deprecatedValidateParticipation.ts | 2 +- .../deprecatedValidateSafetyDepositBoxV1.ts | 2 +- .../models/metaplex/emptyPaymentAccount.ts | 2 +- .../src/models/metaplex/index.ts | 45 +++++++++++-------- .../models/metaplex/initAuctionManagerV2.ts | 2 +- .../src/models/metaplex/redeemBid.ts | 14 +++--- .../metaplex/redeemFullRightsTransferBid.ts | 14 +++--- .../metaplex/redeemParticipationBidV3.ts | 16 +++---- .../models/metaplex/redeemPrintingV2Bid.ts | 10 +---- .../src/models/metaplex/setStore.ts | 2 +- .../models/metaplex/setWhitelistedCreator.ts | 2 +- .../src/models/metaplex/startAuction.ts | 2 +- .../metaplex/validateSafetyDepositBoxV2.ts | 2 +- .../models/metaplex/withdrawMasterEdition.ts | 16 +++---- .../web/src/actions/addTokensToVault.ts | 12 ++--- js/packages/web/src/actions/cancelBid.ts | 5 ++- .../web/src/actions/claimUnusedPrizes.ts | 8 ++-- js/packages/web/src/actions/closeVault.ts | 17 ++++--- .../web/src/actions/createAuctionManager.ts | 12 +++-- .../src/actions/createExternalPriceAccount.ts | 14 +++--- js/packages/web/src/actions/createVault.ts | 9 ++-- .../decommAuctionManagerAndReturnPrizes.ts | 4 +- ...precatedCreateReservationListsForTokens.ts | 2 +- js/packages/web/src/actions/makeAuction.ts | 7 +-- js/packages/web/src/actions/saveAdmin.ts | 6 +-- js/packages/web/src/actions/sendPlaceBid.ts | 10 ++--- js/packages/web/src/actions/sendRedeemBid.ts | 21 ++++----- js/packages/web/src/actions/settle.ts | 4 +- .../web/src/actions/startAuctionManually.ts | 2 +- .../web/src/components/AuctionCard/index.tsx | 2 +- .../contexts/meta/isMetadataPartOfStore.ts | 5 ++- .../web/src/contexts/meta/loadAccounts.ts | 24 +++++----- .../contexts/meta/processMetaplexAccounts.ts | 4 +- js/packages/web/src/contexts/meta/types.ts | 4 +- js/packages/web/src/hooks/useArt.ts | 2 +- js/packages/web/src/hooks/useAuctions.ts | 13 ++---- js/packages/web/src/hooks/useUserArts.ts | 2 +- js/packages/web/src/views/admin/index.tsx | 2 +- js/packages/web/src/views/analytics/index.tsx | 3 +- js/packages/web/src/views/auction/billing.tsx | 2 +- js/packages/web/src/views/auction/index.tsx | 2 +- .../web/src/views/auctionCreate/index.tsx | 5 ++- js/packages/web/src/views/home/setup.tsx | 4 +- 52 files changed, 181 insertions(+), 193 deletions(-) rename js/packages/{web => common}/src/models/metaplex/claimBid.ts (94%) rename js/packages/{web => common}/src/models/metaplex/decommissionAuctionManager.ts (95%) rename js/packages/{web => common}/src/models/metaplex/deprecatedInitAuctionManagerV1.ts (96%) rename js/packages/{web => common}/src/models/metaplex/deprecatedPopulateParticipationPrintingAccount.ts (97%) rename js/packages/{web => common}/src/models/metaplex/deprecatedRedeemParticipationBid.ts (97%) rename js/packages/{web => common}/src/models/metaplex/deprecatedStates.ts (98%) rename js/packages/{web => common}/src/models/metaplex/deprecatedValidateParticipation.ts (96%) rename js/packages/{web => common}/src/models/metaplex/deprecatedValidateSafetyDepositBoxV1.ts (97%) rename js/packages/{web => common}/src/models/metaplex/emptyPaymentAccount.ts (97%) rename js/packages/{web => common}/src/models/metaplex/index.ts (98%) rename js/packages/{web => common}/src/models/metaplex/initAuctionManagerV2.ts (96%) rename js/packages/{web => common}/src/models/metaplex/redeemBid.ts (98%) rename js/packages/{web => common}/src/models/metaplex/redeemFullRightsTransferBid.ts (98%) rename js/packages/{web => common}/src/models/metaplex/redeemParticipationBidV3.ts (98%) rename js/packages/{web => common}/src/models/metaplex/redeemPrintingV2Bid.ts (96%) rename js/packages/{web => common}/src/models/metaplex/setStore.ts (95%) rename js/packages/{web => common}/src/models/metaplex/setWhitelistedCreator.ts (95%) rename js/packages/{web => common}/src/models/metaplex/startAuction.ts (94%) rename js/packages/{web => common}/src/models/metaplex/validateSafetyDepositBoxV2.ts (97%) rename js/packages/{web => common}/src/models/metaplex/withdrawMasterEdition.ts (97%) diff --git a/js/packages/common/src/actions/auction.ts b/js/packages/common/src/actions/auction.ts index c01a12c..abab2ed 100644 --- a/js/packages/common/src/actions/auction.ts +++ b/js/packages/common/src/actions/auction.ts @@ -672,7 +672,7 @@ export async function createAuction( ); } -export async function startAuction( +export async function startAuctionWithResource( resource: StringPublicKey, creator: StringPublicKey, instructions: TransactionInstruction[], diff --git a/js/packages/common/src/contexts/index.tsx b/js/packages/common/src/contexts/index.tsx index 05fbc46..3842422 100644 --- a/js/packages/common/src/contexts/index.tsx +++ b/js/packages/common/src/contexts/index.tsx @@ -4,5 +4,4 @@ export * as Connection from './connection'; export * from './connection'; export * as Wallet from './wallet'; export * from './wallet'; -export * as Store from './store'; export * from './store'; diff --git a/js/packages/common/src/models/index.ts b/js/packages/common/src/models/index.ts index 362a768..5f3bcc6 100644 --- a/js/packages/common/src/models/index.ts +++ b/js/packages/common/src/models/index.ts @@ -1 +1,2 @@ export * from './account'; +export * from './metaplex'; diff --git a/js/packages/web/src/models/metaplex/claimBid.ts b/js/packages/common/src/models/metaplex/claimBid.ts similarity index 94% rename from js/packages/web/src/models/metaplex/claimBid.ts rename to js/packages/common/src/models/metaplex/claimBid.ts index d9f9141..a36c045 100644 --- a/js/packages/web/src/models/metaplex/claimBid.ts +++ b/js/packages/common/src/models/metaplex/claimBid.ts @@ -1,13 +1,9 @@ -import { - getBidderPotKey, - programIds, - StringPublicKey, - toPublicKey, -} from '@oyster/common'; import { SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; import { getAuctionKeys, ClaimBidArgs, SCHEMA } from '.'; +import { getBidderPotKey } from '../../actions'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function claimBid( acceptPayment: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/decommissionAuctionManager.ts b/js/packages/common/src/models/metaplex/decommissionAuctionManager.ts similarity index 95% rename from js/packages/web/src/models/metaplex/decommissionAuctionManager.ts rename to js/packages/common/src/models/metaplex/decommissionAuctionManager.ts index 1279bb7..4c48bc8 100644 --- a/js/packages/web/src/models/metaplex/decommissionAuctionManager.ts +++ b/js/packages/common/src/models/metaplex/decommissionAuctionManager.ts @@ -1,8 +1,8 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; import { DecommissionAuctionManagerArgs, SCHEMA } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function decommissionAuctionManager( auctionManager: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/deprecatedInitAuctionManagerV1.ts b/js/packages/common/src/models/metaplex/deprecatedInitAuctionManagerV1.ts similarity index 96% rename from js/packages/web/src/models/metaplex/deprecatedInitAuctionManagerV1.ts rename to js/packages/common/src/models/metaplex/deprecatedInitAuctionManagerV1.ts index af2cb55..cd65f3e 100644 --- a/js/packages/web/src/models/metaplex/deprecatedInitAuctionManagerV1.ts +++ b/js/packages/common/src/models/metaplex/deprecatedInitAuctionManagerV1.ts @@ -1,4 +1,3 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -7,6 +6,7 @@ import { import { serialize } from 'borsh'; import { getAuctionKeys, SCHEMA } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; import { AuctionManagerSettingsV1, DeprecatedInitAuctionManagerV1Args, diff --git a/js/packages/web/src/models/metaplex/deprecatedPopulateParticipationPrintingAccount.ts b/js/packages/common/src/models/metaplex/deprecatedPopulateParticipationPrintingAccount.ts similarity index 97% rename from js/packages/web/src/models/metaplex/deprecatedPopulateParticipationPrintingAccount.ts rename to js/packages/common/src/models/metaplex/deprecatedPopulateParticipationPrintingAccount.ts index e6a9401..17b5387 100644 --- a/js/packages/web/src/models/metaplex/deprecatedPopulateParticipationPrintingAccount.ts +++ b/js/packages/common/src/models/metaplex/deprecatedPopulateParticipationPrintingAccount.ts @@ -1,15 +1,13 @@ -import { - programIds, - VAULT_PREFIX, - getAuctionExtended, - findProgramAddress, - StringPublicKey, - toPublicKey, -} from '@oyster/common'; import { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; - import { SCHEMA } from '.'; +import { getAuctionExtended, VAULT_PREFIX } from '../../actions'; +import { + findProgramAddress, + programIds, + StringPublicKey, + toPublicKey, +} from '../../utils'; import { DeprecatedPopulateParticipationPrintingAccountArgs } from './deprecatedStates'; export async function deprecatedPopulateParticipationPrintingAccount( diff --git a/js/packages/web/src/models/metaplex/deprecatedRedeemParticipationBid.ts b/js/packages/common/src/models/metaplex/deprecatedRedeemParticipationBid.ts similarity index 97% rename from js/packages/web/src/models/metaplex/deprecatedRedeemParticipationBid.ts rename to js/packages/common/src/models/metaplex/deprecatedRedeemParticipationBid.ts index 69fb542..0ab4d95 100644 --- a/js/packages/web/src/models/metaplex/deprecatedRedeemParticipationBid.ts +++ b/js/packages/common/src/models/metaplex/deprecatedRedeemParticipationBid.ts @@ -1,4 +1,3 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -12,6 +11,7 @@ import { SCHEMA, getSafetyDepositConfig, } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; import { DeprecatedRedeemParticipationBidArgs } from './deprecatedStates'; export async function deprecatedRedeemParticipationBid( diff --git a/js/packages/web/src/models/metaplex/deprecatedStates.ts b/js/packages/common/src/models/metaplex/deprecatedStates.ts similarity index 98% rename from js/packages/web/src/models/metaplex/deprecatedStates.ts rename to js/packages/common/src/models/metaplex/deprecatedStates.ts index 37041ba..fecf8d7 100644 --- a/js/packages/web/src/models/metaplex/deprecatedStates.ts +++ b/js/packages/common/src/models/metaplex/deprecatedStates.ts @@ -1,4 +1,3 @@ -import { programIds, findProgramAddress, toPublicKey } from '@oyster/common'; import BN from 'bn.js'; import { AuctionManagerStatus, @@ -9,6 +8,7 @@ import { WinningConfigType, WinningConstraint, } from '.'; +import { findProgramAddress, programIds, toPublicKey } from '../../utils'; export const MAX_BID_REDEMPTION_TICKET_V1_SIZE = 3; diff --git a/js/packages/web/src/models/metaplex/deprecatedValidateParticipation.ts b/js/packages/common/src/models/metaplex/deprecatedValidateParticipation.ts similarity index 96% rename from js/packages/web/src/models/metaplex/deprecatedValidateParticipation.ts rename to js/packages/common/src/models/metaplex/deprecatedValidateParticipation.ts index bcd332f..9f8c77f 100644 --- a/js/packages/web/src/models/metaplex/deprecatedValidateParticipation.ts +++ b/js/packages/common/src/models/metaplex/deprecatedValidateParticipation.ts @@ -1,4 +1,3 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -7,6 +6,7 @@ import { import { serialize } from 'borsh'; import { SCHEMA } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; import { DeprecatedValidateParticipationArgs } from './deprecatedStates'; export async function deprecatedValidateParticipation( diff --git a/js/packages/web/src/models/metaplex/deprecatedValidateSafetyDepositBoxV1.ts b/js/packages/common/src/models/metaplex/deprecatedValidateSafetyDepositBoxV1.ts similarity index 97% rename from js/packages/web/src/models/metaplex/deprecatedValidateSafetyDepositBoxV1.ts rename to js/packages/common/src/models/metaplex/deprecatedValidateSafetyDepositBoxV1.ts index 8b204a9..f985be4 100644 --- a/js/packages/web/src/models/metaplex/deprecatedValidateSafetyDepositBoxV1.ts +++ b/js/packages/common/src/models/metaplex/deprecatedValidateSafetyDepositBoxV1.ts @@ -1,4 +1,3 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -7,6 +6,7 @@ import { import { serialize } from 'borsh'; import { getAuctionKeys, getOriginalAuthority, SCHEMA } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; import { getSafetyDepositBoxValidationTicket, diff --git a/js/packages/web/src/models/metaplex/emptyPaymentAccount.ts b/js/packages/common/src/models/metaplex/emptyPaymentAccount.ts similarity index 97% rename from js/packages/web/src/models/metaplex/emptyPaymentAccount.ts rename to js/packages/common/src/models/metaplex/emptyPaymentAccount.ts index c86b379..aac9a8a 100644 --- a/js/packages/web/src/models/metaplex/emptyPaymentAccount.ts +++ b/js/packages/common/src/models/metaplex/emptyPaymentAccount.ts @@ -1,4 +1,3 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -13,6 +12,7 @@ import { getSafetyDepositConfig, SCHEMA, } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function emptyPaymentAccount( acceptPayment: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/index.ts b/js/packages/common/src/models/metaplex/index.ts similarity index 98% rename from js/packages/web/src/models/metaplex/index.ts rename to js/packages/common/src/models/metaplex/index.ts index bfb62a2..47524e9 100644 --- a/js/packages/web/src/models/metaplex/index.ts +++ b/js/packages/common/src/models/metaplex/index.ts @@ -1,24 +1,24 @@ -import { - AUCTION_PREFIX, - programIds, - METADATA, - AccountParser, - findProgramAddress, - AuctionData, - ParsedAccount, - Vault, - Metadata, - MasterEditionV1, - SafetyDepositBox, - MasterEditionV2, - toPublicKey, - StringPublicKey, -} from '@oyster/common'; import { AccountInfo, SystemProgram } from '@solana/web3.js'; import BN from 'bn.js'; -import { deserializeUnchecked } from 'borsh'; import bs58 from 'bs58'; -import { AuctionViewItem } from '../../hooks'; +import { deserializeUnchecked } from 'borsh'; +import { + AuctionData, + AUCTION_PREFIX, + MasterEditionV1, + MasterEditionV2, + METADATA, + Metadata, + SafetyDepositBox, + Vault, +} from '../../actions'; +import { AccountParser, ParsedAccount } from '../../contexts'; +import { + findProgramAddress, + programIds, + toPublicKey, + StringPublicKey, +} from '../../utils'; import { AuctionManagerV1, BidRedemptionTicketV1, @@ -35,6 +35,7 @@ export * from './deprecatedValidateSafetyDepositBoxV1'; export * from './redeemParticipationBidV3'; export * from './redeemPrintingV2Bid'; export * from './withdrawMasterEdition'; +export * from './deprecatedStates'; export const METAPLEX_PREFIX = 'metaplex'; export const TOTALS = 'totals'; @@ -537,6 +538,14 @@ export interface BidRedemptionTicket { getBidRedeemed(order: number): boolean; } + +export interface AuctionViewItem { + winningConfigType: WinningConfigType; + amount: BN; + metadata: ParsedAccount; + safetyDeposit: ParsedAccount; + masterEdition?: ParsedAccount; +} export class BidRedemptionTicketV2 implements BidRedemptionTicket { key: MetaplexKey = MetaplexKey.BidRedemptionTicketV2; winnerIndex: BN | null; diff --git a/js/packages/web/src/models/metaplex/initAuctionManagerV2.ts b/js/packages/common/src/models/metaplex/initAuctionManagerV2.ts similarity index 96% rename from js/packages/web/src/models/metaplex/initAuctionManagerV2.ts rename to js/packages/common/src/models/metaplex/initAuctionManagerV2.ts index 11a8543..9e76393 100644 --- a/js/packages/web/src/models/metaplex/initAuctionManagerV2.ts +++ b/js/packages/common/src/models/metaplex/initAuctionManagerV2.ts @@ -1,4 +1,3 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -14,6 +13,7 @@ import { SCHEMA, TupleNumericType, } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function initAuctionManagerV2( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/redeemBid.ts b/js/packages/common/src/models/metaplex/redeemBid.ts similarity index 98% rename from js/packages/web/src/models/metaplex/redeemBid.ts rename to js/packages/common/src/models/metaplex/redeemBid.ts index f5b4287..9f1ad94 100644 --- a/js/packages/web/src/models/metaplex/redeemBid.ts +++ b/js/packages/common/src/models/metaplex/redeemBid.ts @@ -1,10 +1,3 @@ -import { - findProgramAddress, - programIds, - StringPublicKey, - toPublicKey, - VAULT_PREFIX, -} from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -21,6 +14,13 @@ import { RedeemUnusedWinningConfigItemsAsAuctioneerArgs, SCHEMA, } from '.'; +import { VAULT_PREFIX } from '../../actions'; +import { + findProgramAddress, + programIds, + StringPublicKey, + toPublicKey, +} from '../../utils'; export async function redeemBid( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/redeemFullRightsTransferBid.ts b/js/packages/common/src/models/metaplex/redeemFullRightsTransferBid.ts similarity index 98% rename from js/packages/web/src/models/metaplex/redeemFullRightsTransferBid.ts rename to js/packages/common/src/models/metaplex/redeemFullRightsTransferBid.ts index 29ae2c9..64377f9 100644 --- a/js/packages/web/src/models/metaplex/redeemFullRightsTransferBid.ts +++ b/js/packages/common/src/models/metaplex/redeemFullRightsTransferBid.ts @@ -1,10 +1,3 @@ -import { - programIds, - VAULT_PREFIX, - findProgramAddress, - StringPublicKey, - toPublicKey, -} from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -21,6 +14,13 @@ import { RedeemUnusedWinningConfigItemsAsAuctioneerArgs, SCHEMA, } from '.'; +import { VAULT_PREFIX } from '../../actions'; +import { + findProgramAddress, + programIds, + StringPublicKey, + toPublicKey, +} from '../../utils'; export async function redeemFullRightsTransferBid( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/redeemParticipationBidV3.ts b/js/packages/common/src/models/metaplex/redeemParticipationBidV3.ts similarity index 98% rename from js/packages/web/src/models/metaplex/redeemParticipationBidV3.ts rename to js/packages/common/src/models/metaplex/redeemParticipationBidV3.ts index 4922d0a..b46cdfc 100644 --- a/js/packages/web/src/models/metaplex/redeemParticipationBidV3.ts +++ b/js/packages/common/src/models/metaplex/redeemParticipationBidV3.ts @@ -1,12 +1,3 @@ -import { - getEdition, - programIds, - getMetadata, - getEditionMarkPda, - getAuctionExtended, - StringPublicKey, - toPublicKey, -} from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -23,6 +14,13 @@ import { getPrizeTrackingTicket, getSafetyDepositConfig, } from '.'; +import { + getAuctionExtended, + getEdition, + getEditionMarkPda, + getMetadata, +} from '../../actions'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function redeemParticipationBidV3( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/redeemPrintingV2Bid.ts b/js/packages/common/src/models/metaplex/redeemPrintingV2Bid.ts similarity index 96% rename from js/packages/web/src/models/metaplex/redeemPrintingV2Bid.ts rename to js/packages/common/src/models/metaplex/redeemPrintingV2Bid.ts index 8384982..3ed4c15 100644 --- a/js/packages/web/src/models/metaplex/redeemPrintingV2Bid.ts +++ b/js/packages/common/src/models/metaplex/redeemPrintingV2Bid.ts @@ -1,11 +1,3 @@ -import { - getEdition, - getEditionMarkPda, - getMetadata, - programIds, - StringPublicKey, - toPublicKey, -} from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -22,6 +14,8 @@ import { SCHEMA, getSafetyDepositConfig, } from '.'; +import { getEdition, getEditionMarkPda, getMetadata } from '../../actions'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function redeemPrintingV2Bid( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/setStore.ts b/js/packages/common/src/models/metaplex/setStore.ts similarity index 95% rename from js/packages/web/src/models/metaplex/setStore.ts rename to js/packages/common/src/models/metaplex/setStore.ts index de5defa..b381683 100644 --- a/js/packages/web/src/models/metaplex/setStore.ts +++ b/js/packages/common/src/models/metaplex/setStore.ts @@ -1,8 +1,8 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; import { SCHEMA, SetStoreArgs } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function setStore( isPublic: boolean, diff --git a/js/packages/web/src/models/metaplex/setWhitelistedCreator.ts b/js/packages/common/src/models/metaplex/setWhitelistedCreator.ts similarity index 95% rename from js/packages/web/src/models/metaplex/setWhitelistedCreator.ts rename to js/packages/common/src/models/metaplex/setWhitelistedCreator.ts index 8d56c2f..1f9768d 100644 --- a/js/packages/web/src/models/metaplex/setWhitelistedCreator.ts +++ b/js/packages/common/src/models/metaplex/setWhitelistedCreator.ts @@ -1,8 +1,8 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; import { getWhitelistedCreator, SCHEMA, SetWhitelistedCreatorArgs } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function setWhitelistedCreator( creator: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/startAuction.ts b/js/packages/common/src/models/metaplex/startAuction.ts similarity index 94% rename from js/packages/web/src/models/metaplex/startAuction.ts rename to js/packages/common/src/models/metaplex/startAuction.ts index d5ad758..9e75802 100644 --- a/js/packages/web/src/models/metaplex/startAuction.ts +++ b/js/packages/common/src/models/metaplex/startAuction.ts @@ -1,8 +1,8 @@ -import { programIds, StringPublicKey, toPublicKey } from '@oyster/common'; import { SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; import { getAuctionKeys, SCHEMA, StartAuctionArgs } from '.'; +import { programIds, StringPublicKey, toPublicKey } from '../../utils'; export async function startAuction( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/validateSafetyDepositBoxV2.ts b/js/packages/common/src/models/metaplex/validateSafetyDepositBoxV2.ts similarity index 97% rename from js/packages/web/src/models/metaplex/validateSafetyDepositBoxV2.ts rename to js/packages/common/src/models/metaplex/validateSafetyDepositBoxV2.ts index 79aa063..d671435 100644 --- a/js/packages/web/src/models/metaplex/validateSafetyDepositBoxV2.ts +++ b/js/packages/common/src/models/metaplex/validateSafetyDepositBoxV2.ts @@ -1,4 +1,3 @@ -import { programIds, toPublicKey, StringPublicKey } from '@oyster/common'; import { SystemProgram, SYSVAR_RENT_PUBKEY, @@ -15,6 +14,7 @@ import { SCHEMA, ValidateSafetyDepositBoxV2Args, } from '.'; +import { programIds, toPublicKey, StringPublicKey } from '../../utils'; export async function validateSafetyDepositBoxV2( vault: StringPublicKey, diff --git a/js/packages/web/src/models/metaplex/withdrawMasterEdition.ts b/js/packages/common/src/models/metaplex/withdrawMasterEdition.ts similarity index 97% rename from js/packages/web/src/models/metaplex/withdrawMasterEdition.ts rename to js/packages/common/src/models/metaplex/withdrawMasterEdition.ts index 76f652c..9c6053e 100644 --- a/js/packages/web/src/models/metaplex/withdrawMasterEdition.ts +++ b/js/packages/common/src/models/metaplex/withdrawMasterEdition.ts @@ -1,12 +1,3 @@ -import { - AUCTION_PREFIX, - EXTENDED, - findProgramAddress, - programIds, - StringPublicKey, - toPublicKey, - VAULT_PREFIX, -} from '@oyster/common'; import { SYSVAR_RENT_PUBKEY, TransactionInstruction } from '@solana/web3.js'; import { serialize } from 'borsh'; @@ -17,6 +8,13 @@ import { getPrizeTrackingTicket, getSafetyDepositConfig, } from '.'; +import { AUCTION_PREFIX, EXTENDED, VAULT_PREFIX } from '../../actions'; +import { + findProgramAddress, + programIds, + toPublicKey, + StringPublicKey, +} from '../../utils'; export async function withdrawMasterEdition( vault: StringPublicKey, diff --git a/js/packages/web/src/actions/addTokensToVault.ts b/js/packages/web/src/actions/addTokensToVault.ts index 73162fb..d83cedc 100644 --- a/js/packages/web/src/actions/addTokensToVault.ts +++ b/js/packages/web/src/actions/addTokensToVault.ts @@ -1,22 +1,24 @@ import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js'; import { utils, - actions, - models, findProgramAddress, MetadataKey, StringPublicKey, toPublicKey, WalletSigner, } from '@oyster/common'; +import { SafetyDepositConfig } from '@oyster/common/dist/lib/models/metaplex/index'; +import { approve } from '@oyster/common/dist/lib/models/account'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; +import { + addTokenToInactiveVault, + VAULT_PREFIX, +} from '@oyster/common/dist/lib/actions/vault'; import { AccountLayout } from '@solana/spl-token'; import BN from 'bn.js'; import { SafetyDepositDraft } from './createAuctionManager'; -import { SafetyDepositConfig } from '../models/metaplex'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; -const { createTokenAccount, addTokenToInactiveVault, VAULT_PREFIX } = actions; -const { approve } = models; export interface SafetyDepositInstructionTemplate { box: { diff --git a/js/packages/web/src/actions/cancelBid.ts b/js/packages/web/src/actions/cancelBid.ts index 20ab562..c33f13d 100644 --- a/js/packages/web/src/actions/cancelBid.ts +++ b/js/packages/web/src/actions/cancelBid.ts @@ -16,7 +16,10 @@ import { import { AccountLayout } from '@solana/spl-token'; import { TransactionInstruction, Keypair, Connection } from '@solana/web3.js'; import { AuctionView } from '../hooks'; -import { BidRedemptionTicket, PrizeTrackingTicket } from '../models/metaplex'; +import { + BidRedemptionTicket, + PrizeTrackingTicket, +} from '@oyster/common/dist/lib/models/metaplex/index'; import { claimUnusedPrizes } from './claimUnusedPrizes'; import { setupPlaceBid } from './sendPlaceBid'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; diff --git a/js/packages/web/src/actions/claimUnusedPrizes.ts b/js/packages/web/src/actions/claimUnusedPrizes.ts index 0f74ebf..d01804e 100644 --- a/js/packages/web/src/actions/claimUnusedPrizes.ts +++ b/js/packages/web/src/actions/claimUnusedPrizes.ts @@ -1,6 +1,5 @@ import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js'; import { - actions, ParsedAccount, TokenAccount, SafetyDepositBox, @@ -16,7 +15,7 @@ import { } from '@oyster/common'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; import { AccountLayout, MintLayout } from '@solana/spl-token'; -import { AuctionView, AuctionViewItem } from '../hooks'; +import { AuctionView } from '../hooks'; import { WinningConfigType, redeemBid, @@ -25,13 +24,14 @@ import { BidRedemptionTicket, getBidRedemption, PrizeTrackingTicket, -} from '../models/metaplex'; + AuctionViewItem, +} from '@oyster/common/dist/lib/models/metaplex/index'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; import { eligibleForParticipationPrizeGivenWinningIndex, setupRedeemParticipationInstructions, setupRedeemPrintingV2Instructions, } from './sendRedeemBid'; -const { createTokenAccount } = actions; export async function findEligibleParticipationBidsForRedemption( auctionView: AuctionView, diff --git a/js/packages/web/src/actions/closeVault.ts b/js/packages/web/src/actions/closeVault.ts index eb5ac8e..2eb2e44 100644 --- a/js/packages/web/src/actions/closeVault.ts +++ b/js/packages/web/src/actions/closeVault.ts @@ -1,16 +1,15 @@ import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js'; -import { - actions, - models, - StringPublicKey, - toPublicKey, - WalletSigner, -} from '@oyster/common'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; +import { StringPublicKey, toPublicKey, WalletSigner } from '@oyster/common'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; +import { + activateVault, + combineVault, +} from '@oyster/common/dist/lib/actions/vault'; +import { approve } from '@oyster/common/dist/lib/models/account'; + import { AccountLayout } from '@solana/spl-token'; import BN from 'bn.js'; -const { createTokenAccount, activateVault, combineVault } = actions; -const { approve } = models; // This command "closes" the vault, by activating & combining it in one go, handing it over to the auction manager // authority (that may or may not exist yet.) diff --git a/js/packages/web/src/actions/createAuctionManager.ts b/js/packages/web/src/actions/createAuctionManager.ts index 910aa87..e44cc28 100644 --- a/js/packages/web/src/actions/createAuctionManager.ts +++ b/js/packages/web/src/actions/createAuctionManager.ts @@ -5,7 +5,6 @@ import { SystemProgram, } from '@solana/web3.js'; import { - actions, Metadata, ParsedAccount, MasterEditionV1, @@ -41,7 +40,8 @@ import { TupleNumericType, SafetyDepositConfig, ParticipationStateV2, -} from '../models/metaplex'; +} from '@oyster/common/dist/lib/models/metaplex/index'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; import { createVault } from './createVault'; import { closeVault } from './closeVault'; import { @@ -50,15 +50,13 @@ import { } from './addTokensToVault'; import { makeAuction } from './makeAuction'; import { createExternalPriceAccount } from './createExternalPriceAccount'; -import { deprecatedValidateParticipation } from '../models/metaplex/deprecatedValidateParticipation'; +import { deprecatedValidateParticipation } from '@oyster/common/dist/lib/models/metaplex/deprecatedValidateParticipation'; import { deprecatedCreateReservationListForTokens } from './deprecatedCreateReservationListsForTokens'; import { deprecatedPopulatePrintingTokens } from './deprecatedPopulatePrintingTokens'; import { setVaultAndAuctionAuthorities } from './setVaultAndAuctionAuthorities'; import { markItemsThatArentMineAsSold } from './markItemsThatArentMineAsSold'; -import { validateSafetyDepositBoxV2 } from '../models/metaplex/validateSafetyDepositBoxV2'; -import { initAuctionManagerV2 } from '../models/metaplex/initAuctionManagerV2'; - -const { createTokenAccount } = actions; +import { validateSafetyDepositBoxV2 } from '@oyster/common/dist/lib/models/metaplex/validateSafetyDepositBoxV2'; +import { initAuctionManagerV2 } from '@oyster/common/dist/lib/models/metaplex/initAuctionManagerV2'; interface normalPattern { instructions: TransactionInstruction[]; diff --git a/js/packages/web/src/actions/createExternalPriceAccount.ts b/js/packages/web/src/actions/createExternalPriceAccount.ts index 7dc1e01..f1b47b5 100644 --- a/js/packages/web/src/actions/createExternalPriceAccount.ts +++ b/js/packages/web/src/actions/createExternalPriceAccount.ts @@ -4,22 +4,22 @@ import { SystemProgram, TransactionInstruction, } from '@solana/web3.js'; +import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; + import { utils, - actions, StringPublicKey, toPublicKey, WalletSigner, } from '@oyster/common'; -import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; -import BN from 'bn.js'; -import { QUOTE_MINT } from '../constants'; - -const { +import { updateExternalPriceAccount, ExternalPriceAccount, MAX_EXTERNAL_ACCOUNT_SIZE, -} = actions; +} from '@oyster/common/dist/lib/actions/vault'; + +import BN from 'bn.js'; +import { QUOTE_MINT } from '../constants'; // This command creates the external pricing oracle export async function createExternalPriceAccount( diff --git a/js/packages/web/src/actions/createVault.ts b/js/packages/web/src/actions/createVault.ts index 0ba20f5..6268a26 100644 --- a/js/packages/web/src/actions/createVault.ts +++ b/js/packages/web/src/actions/createVault.ts @@ -6,19 +6,22 @@ import { } from '@solana/web3.js'; import { utils, - actions, createMint, findProgramAddress, StringPublicKey, toPublicKey, WalletSigner, } from '@oyster/common'; +import { + initVault, + MAX_VAULT_SIZE, + VAULT_PREFIX, +} from '@oyster/common/dist/lib/actions/vault'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; import { AccountLayout, MintLayout } from '@solana/spl-token'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; -const { createTokenAccount, initVault, MAX_VAULT_SIZE, VAULT_PREFIX } = actions; - // This command creates the external pricing oracle a vault // This gets the vault ready for adding the tokens. export async function createVault( diff --git a/js/packages/web/src/actions/decommAuctionManagerAndReturnPrizes.ts b/js/packages/web/src/actions/decommAuctionManagerAndReturnPrizes.ts index 084b7ca..cc02298 100644 --- a/js/packages/web/src/actions/decommAuctionManagerAndReturnPrizes.ts +++ b/js/packages/web/src/actions/decommAuctionManagerAndReturnPrizes.ts @@ -9,8 +9,8 @@ import { } from '@oyster/common'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; import { AuctionView } from '../hooks'; -import { AuctionManagerStatus } from '../models/metaplex'; -import { decommissionAuctionManager } from '../models/metaplex/decommissionAuctionManager'; +import { AuctionManagerStatus } from '@oyster/common/dist/lib/models/metaplex/index'; +import { decommissionAuctionManager } from '@oyster/common/dist/lib/models/metaplex/decommissionAuctionManager'; import { unwindVault } from './unwindVault'; export async function decommAuctionManagerAndReturnPrizes( diff --git a/js/packages/web/src/actions/deprecatedCreateReservationListsForTokens.ts b/js/packages/web/src/actions/deprecatedCreateReservationListsForTokens.ts index 63372f5..5a59f3e 100644 --- a/js/packages/web/src/actions/deprecatedCreateReservationListsForTokens.ts +++ b/js/packages/web/src/actions/deprecatedCreateReservationListsForTokens.ts @@ -6,7 +6,7 @@ import { } from '@oyster/common'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; import { SafetyDepositInstructionTemplate } from './addTokensToVault'; -import { WinningConfigType } from '../models/metaplex'; +import { WinningConfigType } from '@oyster/common/dist/lib/models/metaplex/index'; const BATCH_SIZE = 10; // This command batches out creating reservation lists for those tokens who are being sold in PrintingV1 mode. diff --git a/js/packages/web/src/actions/makeAuction.ts b/js/packages/web/src/actions/makeAuction.ts index 1e71385..c38d22f 100644 --- a/js/packages/web/src/actions/makeAuction.ts +++ b/js/packages/web/src/actions/makeAuction.ts @@ -1,7 +1,6 @@ import { Keypair, TransactionInstruction } from '@solana/web3.js'; import { utils, - actions, findProgramAddress, IPartialCreateAuctionArgs, CreateAuctionArgs, @@ -9,10 +8,12 @@ import { toPublicKey, WalletSigner, } from '@oyster/common'; +import { + AUCTION_PREFIX, + createAuction, +} from '@oyster/common/dist/lib/actions/auction'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; -const { AUCTION_PREFIX, createAuction } = actions; - // This command makes an auction export async function makeAuction( wallet: WalletSigner, diff --git a/js/packages/web/src/actions/saveAdmin.ts b/js/packages/web/src/actions/saveAdmin.ts index 95cf681..930cc93 100644 --- a/js/packages/web/src/actions/saveAdmin.ts +++ b/js/packages/web/src/actions/saveAdmin.ts @@ -5,10 +5,10 @@ import { sendTransactionWithRetry, WalletSigner, } from '@oyster/common'; +import { WhitelistedCreator } from '@oyster/common/dist/lib/models/metaplex/index'; +import { setStore } from '@oyster/common/dist/lib/models/metaplex/setStore'; +import { setWhitelistedCreator } from '@oyster/common/dist/lib/models/metaplex/setWhitelistedCreator'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; -import { WhitelistedCreator } from '../models/metaplex'; -import { setStore } from '../models/metaplex/setStore'; -import { setWhitelistedCreator } from '../models/metaplex/setWhitelistedCreator'; // TODO if this becomes very slow move to batching txns like we do with settle.ts // but given how little this should be used keep it simple diff --git a/js/packages/web/src/actions/sendPlaceBid.ts b/js/packages/web/src/actions/sendPlaceBid.ts index e2d6028..784b6be 100644 --- a/js/packages/web/src/actions/sendPlaceBid.ts +++ b/js/packages/web/src/actions/sendPlaceBid.ts @@ -1,11 +1,8 @@ import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js'; import { - actions, sendTransactionWithRetry, placeBid, - models, cache, - TokenAccount, ensureWrappedAccount, toLamports, ParsedAccount, @@ -13,15 +10,16 @@ import { WalletSigner, } from '@oyster/common'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; +import { approve } from '@oyster/common/dist/lib/models/account'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; +import { TokenAccount } from '@oyster/common/dist/lib/models/account'; + import { AccountLayout, MintInfo } from '@solana/spl-token'; import { AuctionView } from '../hooks'; import BN from 'bn.js'; import { setupCancelBid } from './cancelBid'; import { QUOTE_MINT } from '../constants'; -const { createTokenAccount } = actions; -const { approve } = models; - export async function sendPlaceBid( connection: Connection, wallet: WalletSigner, diff --git a/js/packages/web/src/actions/sendRedeemBid.ts b/js/packages/web/src/actions/sendRedeemBid.ts index d2ccd93..5983bfe 100644 --- a/js/packages/web/src/actions/sendRedeemBid.ts +++ b/js/packages/web/src/actions/sendRedeemBid.ts @@ -1,9 +1,7 @@ import { Keypair, Connection, TransactionInstruction } from '@solana/web3.js'; import { - actions, ParsedAccount, programIds, - models, TokenAccount, createMint, SafetyDepositBox, @@ -28,8 +26,10 @@ import { } from '@oyster/common'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; import { AccountLayout, MintLayout, Token } from '@solana/spl-token'; -import { AuctionView, AuctionViewItem } from '../hooks'; +import { AuctionView } from '../hooks'; import { + AuctionManagerV1, + ParticipationStateV1, WinningConfigType, NonWinningConstraint, redeemBid, @@ -41,21 +41,18 @@ import { PrizeTrackingTicket, getPrizeTrackingTicket, BidRedemptionTicket, -} from '../models/metaplex'; -import { claimBid } from '../models/metaplex/claimBid'; + AuctionViewItem, +} from '@oyster/common/dist/lib/models/metaplex/index'; +import { claimBid } from '@oyster/common/dist/lib/models/metaplex/claimBid'; +import { approve } from '@oyster/common/dist/lib/models/account'; +import { createTokenAccount } from '@oyster/common/dist/lib/actions/account'; import { setupCancelBid } from './cancelBid'; -import { deprecatedPopulateParticipationPrintingAccount } from '../models/metaplex/deprecatedPopulateParticipationPrintingAccount'; +import { deprecatedPopulateParticipationPrintingAccount } from '@oyster/common/dist/lib/models/metaplex/deprecatedPopulateParticipationPrintingAccount'; import { setupPlaceBid } from './sendPlaceBid'; import { claimUnusedPrizes } from './claimUnusedPrizes'; import { createMintAndAccountWithOne } from './createMintAndAccountWithOne'; import { BN } from 'bn.js'; import { QUOTE_MINT } from '../constants'; -import { - AuctionManagerV1, - ParticipationStateV1, -} from '../models/metaplex/deprecatedStates'; -const { createTokenAccount } = actions; -const { approve } = models; export function eligibleForParticipationPrizeGivenWinningIndex( winnerIndex: number | null, diff --git a/js/packages/web/src/actions/settle.ts b/js/packages/web/src/actions/settle.ts index b165d5b..a8684e4 100644 --- a/js/packages/web/src/actions/settle.ts +++ b/js/packages/web/src/actions/settle.ts @@ -16,8 +16,8 @@ import { import { AuctionView } from '../hooks'; -import { claimBid } from '../models/metaplex/claimBid'; -import { emptyPaymentAccount } from '../models/metaplex/emptyPaymentAccount'; +import { claimBid } from '@oyster/common/dist/lib/models/metaplex/claimBid'; +import { emptyPaymentAccount } from '@oyster/common/dist/lib/models/metaplex/emptyPaymentAccount'; import { QUOTE_MINT } from '../constants'; import { setupPlaceBid } from './sendPlaceBid'; import { WalletNotConnectedError } from '@solana/wallet-adapter-base'; diff --git a/js/packages/web/src/actions/startAuctionManually.ts b/js/packages/web/src/actions/startAuctionManually.ts index ac59450..0315792 100644 --- a/js/packages/web/src/actions/startAuctionManually.ts +++ b/js/packages/web/src/actions/startAuctionManually.ts @@ -1,5 +1,5 @@ import { Connection, Keypair, TransactionInstruction } from '@solana/web3.js'; -import { startAuction } from '../models/metaplex'; +import { startAuction } from '@oyster/common/dist/lib/models/metaplex/index'; import { notify, sendTransactionWithRetry, WalletSigner } from '@oyster/common'; import { AuctionView } from '../hooks'; diff --git a/js/packages/web/src/components/AuctionCard/index.tsx b/js/packages/web/src/components/AuctionCard/index.tsx index e752b29..b698564 100644 --- a/js/packages/web/src/components/AuctionCard/index.tsx +++ b/js/packages/web/src/components/AuctionCard/index.tsx @@ -43,7 +43,7 @@ import { findEligibleParticipationBidsForRedemption } from '../../actions/claimU import { BidRedemptionTicket, MAX_PRIZE_TRACKING_TICKET_SIZE, -} from '../../models/metaplex'; +} from '@oyster/common/dist/lib/models/metaplex/index'; async function calculateTotalCostOfRedeemingOtherPeoplesBids( connection: Connection, diff --git a/js/packages/web/src/contexts/meta/isMetadataPartOfStore.ts b/js/packages/web/src/contexts/meta/isMetadataPartOfStore.ts index ca7dabb..562508f 100644 --- a/js/packages/web/src/contexts/meta/isMetadataPartOfStore.ts +++ b/js/packages/web/src/contexts/meta/isMetadataPartOfStore.ts @@ -1,5 +1,8 @@ import { Metadata, ParsedAccount } from '@oyster/common'; -import { Store, WhitelistedCreator } from '../../models/metaplex'; +import { + Store, + WhitelistedCreator, +} from '@oyster/common/dist/lib/models/metaplex/index'; export const isMetadataPartOfStore = ( m: ParsedAccount, diff --git a/js/packages/web/src/contexts/meta/loadAccounts.ts b/js/packages/web/src/contexts/meta/loadAccounts.ts index f3551ab..f73382f 100644 --- a/js/packages/web/src/contexts/meta/loadAccounts.ts +++ b/js/packages/web/src/contexts/meta/loadAccounts.ts @@ -6,6 +6,17 @@ import { toPublicKey, VAULT_ID, } from '@oyster/common/dist/lib/utils/ids'; +import { getMultipleAccounts, Metadata, ParsedAccount } from '@oyster/common'; +import { MAX_WHITELISTED_CREATOR_SIZE } from '@oyster/common/dist/lib/models/index'; +import { + getEdition, + MAX_CREATOR_LEN, + MAX_CREATOR_LIMIT, + MAX_NAME_LENGTH, + MAX_SYMBOL_LENGTH, + MAX_URI_LENGTH, + METADATA_PREFIX, +} from '@oyster/common/dist/lib/actions/index'; import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'; import { AccountAndPubkey, MetaState, ProcessAccountsFunc } from './types'; import { isMetadataPartOfStore } from './isMetadataPartOfStore'; @@ -13,19 +24,6 @@ import { processAuctions } from './processAuctions'; import { processMetaplexAccounts } from './processMetaplexAccounts'; import { processMetaData } from './processMetaData'; import { processVaultData } from './processVaultData'; -import { - getEdition, - getMultipleAccounts, - MAX_CREATOR_LEN, - MAX_CREATOR_LIMIT, - MAX_NAME_LENGTH, - MAX_SYMBOL_LENGTH, - MAX_URI_LENGTH, - Metadata, - METADATA_PREFIX, - ParsedAccount, -} from '@oyster/common'; -import { MAX_WHITELISTED_CREATOR_SIZE } from '../../models/metaplex'; async function getProgramAccounts( connection: Connection, diff --git a/js/packages/web/src/contexts/meta/processMetaplexAccounts.ts b/js/packages/web/src/contexts/meta/processMetaplexAccounts.ts index 119521a..c4fb51f 100644 --- a/js/packages/web/src/contexts/meta/processMetaplexAccounts.ts +++ b/js/packages/web/src/contexts/meta/processMetaplexAccounts.ts @@ -1,6 +1,7 @@ import { programIds, cache, ParsedAccount, METAPLEX_ID } from '@oyster/common'; import { AccountInfo, PublicKey } from '@solana/web3.js'; import { + AuctionManagerV1, AuctionManagerV2, BidRedemptionTicket, decodeAuctionManager, @@ -19,8 +20,7 @@ import { BidRedemptionTicketV2, decodeSafetyDepositConfig, SafetyDepositConfig, -} from '../../models/metaplex'; -import { AuctionManagerV1 } from '../../models/metaplex/deprecatedStates'; +} from '@oyster/common/dist/lib/models/metaplex/index'; import names from '../../config/userNames.json'; import { ProcessAccountsFunc } from './types'; diff --git a/js/packages/web/src/contexts/meta/types.ts b/js/packages/web/src/contexts/meta/types.ts index a4f9b25..00f6928 100644 --- a/js/packages/web/src/contexts/meta/types.ts +++ b/js/packages/web/src/contexts/meta/types.ts @@ -14,6 +14,7 @@ import { } from '@oyster/common'; import { AccountInfo } from '@solana/web3.js'; import { + AuctionManagerV1, BidRedemptionTicket, Store, WhitelistedCreator, @@ -22,8 +23,7 @@ import { AuctionManagerV2, SafetyDepositConfig, BidRedemptionTicketV2, -} from '../../models/metaplex'; -import { AuctionManagerV1 } from '../../models/metaplex/deprecatedStates'; +} from '@oyster/common/dist/lib/models/metaplex/index'; export interface MetaState { metadata: ParsedAccount[]; diff --git a/js/packages/web/src/hooks/useArt.ts b/js/packages/web/src/hooks/useArt.ts index d676382..6ab9c9d 100644 --- a/js/packages/web/src/hooks/useArt.ts +++ b/js/packages/web/src/hooks/useArt.ts @@ -10,7 +10,7 @@ import { ParsedAccount, StringPublicKey, } from '@oyster/common'; -import { WhitelistedCreator } from '../models/metaplex'; +import { WhitelistedCreator } from '@oyster/common/dist/lib/models/metaplex/index'; import { Cache } from 'three'; import { useInView } from 'react-intersection-observer'; import { pubkeyToString } from '../utils/pubkeyToString'; diff --git a/js/packages/web/src/hooks/useAuctions.ts b/js/packages/web/src/hooks/useAuctions.ts index 299ebf1..4402f7e 100644 --- a/js/packages/web/src/hooks/useAuctions.ts +++ b/js/packages/web/src/hooks/useAuctions.ts @@ -18,14 +18,15 @@ import { useMeta } from '../contexts'; import { AuctionManager, AuctionManagerStatus, + AuctionManagerV1, AuctionManagerV2, BidRedemptionTicket, BidRedemptionTicketV2, getBidderKeys, SafetyDepositConfig, WinningConfigType, -} from '../models/metaplex'; -import { AuctionManagerV1 } from '../models/metaplex/deprecatedStates'; + AuctionViewItem, +} from '@oyster/common/dist/lib/models/metaplex/index'; export enum AuctionViewState { Live = '0', @@ -35,14 +36,6 @@ export enum AuctionViewState { Defective = '-1', } -export interface AuctionViewItem { - winningConfigType: WinningConfigType; - amount: BN; - metadata: ParsedAccount; - safetyDeposit: ParsedAccount; - masterEdition?: ParsedAccount; -} - // Flattened surface item for easy display export interface AuctionView { // items 1:1 with winning configs FOR NOW diff --git a/js/packages/web/src/hooks/useUserArts.ts b/js/packages/web/src/hooks/useUserArts.ts index 58593cc..b86902a 100644 --- a/js/packages/web/src/hooks/useUserArts.ts +++ b/js/packages/web/src/hooks/useUserArts.ts @@ -12,7 +12,7 @@ import { ParticipationConfigV2, WinningConfigType, WinningConstraint, -} from '../models/metaplex'; +} from '@oyster/common/dist/lib/models/metaplex/index'; import { useMeta } from './../contexts'; export const useUserArts = (): SafetyDepositDraft[] => { diff --git a/js/packages/web/src/views/admin/index.tsx b/js/packages/web/src/views/admin/index.tsx index 5cd136b..bac0be1 100644 --- a/js/packages/web/src/views/admin/index.tsx +++ b/js/packages/web/src/views/admin/index.tsx @@ -12,7 +12,7 @@ import { Divider, } from 'antd'; import { useMeta } from '../../contexts'; -import { Store, WhitelistedCreator } from '../../models/metaplex'; +import { Store, WhitelistedCreator } from '@oyster/common/dist/lib/models/metaplex/index'; import { MasterEditionV1, notify, diff --git a/js/packages/web/src/views/analytics/index.tsx b/js/packages/web/src/views/analytics/index.tsx index f93e083..c7f008e 100644 --- a/js/packages/web/src/views/analytics/index.tsx +++ b/js/packages/web/src/views/analytics/index.tsx @@ -1,7 +1,7 @@ import React, { Dispatch, SetStateAction, useState } from 'react'; import { Layout, Button, Col, Spin } from 'antd'; import { useMeta } from '../../contexts'; -import { AuctionManagerV2, WinningConfigType } from '../../models/metaplex'; +import { AuctionManagerV1, AuctionManagerV2, WinningConfigType } from '@oyster/common/dist/lib/models/metaplex/index'; import { Pie, Bar } from 'react-chartjs-2'; import { AuctionDataExtended, @@ -16,7 +16,6 @@ import { import { AuctionView, useAuctions } from '../../hooks'; import { QUOTE_MINT } from '../../constants'; import { MintInfo } from '@solana/spl-token'; -import { AuctionManagerV1 } from '../../models/metaplex/deprecatedStates'; const { Content } = Layout; export const AnalyticsView = () => { diff --git a/js/packages/web/src/views/auction/billing.tsx b/js/packages/web/src/views/auction/billing.tsx index f03cef9..40e7499 100644 --- a/js/packages/web/src/views/auction/billing.tsx +++ b/js/packages/web/src/views/auction/billing.tsx @@ -33,7 +33,7 @@ import { NonWinningConstraint, PayoutTicket, WinningConstraint, -} from '../../models/metaplex'; +} from '@oyster/common/dist/lib/models/metaplex/index'; import { Connection } from '@solana/web3.js'; import { settle } from '../../actions/settle'; import { MintInfo } from '@solana/spl-token'; diff --git a/js/packages/web/src/views/auction/index.tsx b/js/packages/web/src/views/auction/index.tsx index c4f6310..376f169 100644 --- a/js/packages/web/src/views/auction/index.tsx +++ b/js/packages/web/src/views/auction/index.tsx @@ -3,9 +3,9 @@ import { useParams } from 'react-router-dom'; import { Row, Col, Button, Skeleton, Carousel, List, Card } from 'antd'; import { AuctionCard } from '../../components/AuctionCard'; import { Connection } from '@solana/web3.js'; +import { AuctionViewItem } from '@oyster/common/dist/lib/models/metaplex/index'; import { AuctionView as Auction, - AuctionViewItem, useArt, useAuction, useBidsForAuction, diff --git a/js/packages/web/src/views/auctionCreate/index.tsx b/js/packages/web/src/views/auctionCreate/index.tsx index 00fc36b..6117ce1 100644 --- a/js/packages/web/src/views/auctionCreate/index.tsx +++ b/js/packages/web/src/views/auctionCreate/index.tsx @@ -37,7 +37,10 @@ import { useWallet } from '@solana/wallet-adapter-react'; import { MintLayout } from '@solana/spl-token'; import { useHistory, useParams } from 'react-router-dom'; import { capitalize } from 'lodash'; -import { WinningConfigType, AmountRange } from '../../models/metaplex'; +import { + WinningConfigType, + AmountRange, +} from '@oyster/common/dist/lib/models/metaplex/index'; import moment from 'moment'; import { createAuctionManager, diff --git a/js/packages/web/src/views/home/setup.tsx b/js/packages/web/src/views/home/setup.tsx index 358a38d..6228c71 100644 --- a/js/packages/web/src/views/home/setup.tsx +++ b/js/packages/web/src/views/home/setup.tsx @@ -2,7 +2,7 @@ import { useConnection, useStore, useWalletModal, - WalletSigner, + WhitelistedCreator, } from '@oyster/common'; import { useWallet } from '@solana/wallet-adapter-react'; import { Button } from 'antd'; @@ -10,9 +10,7 @@ import { useCallback, useEffect, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { saveAdmin } from '../../actions/saveAdmin'; import { useMeta } from '../../contexts'; -import { WhitelistedCreator } from '../../models/metaplex'; import { SetupVariables } from '../../components/SetupVariables'; -import { WalletAdapter } from '@solana/wallet-adapter-base'; export const SetupView = () => { const [isInitalizingStore, setIsInitalizingStore] = useState(false); From 9e353e924367823fd8a9127fc62854a031fcdb8f Mon Sep 17 00:00:00 2001 From: exromany Date: Tue, 10 Aug 2021 13:14:58 +0300 Subject: [PATCH 02/10] move contexts/meta to common pkg --- js/packages/common/src/contexts/index.tsx | 1 + js/packages/common/src/contexts/meta/index.ts | 10 +++++ .../contexts/meta/isMetadataPartOfStore.ts | 8 ++-- .../src/contexts/meta/loadAccounts.ts | 9 +++-- .../src/contexts/meta/meta.tsx | 20 +++++----- .../src/contexts/meta/onChangeAccount.ts | 0 .../src/contexts/meta/processAuctions.ts | 13 +++---- .../src/contexts/meta/processMetaData.ts | 24 ++++++------ .../contexts/meta/processMetaplexAccounts.ts | 12 ++---- .../src/contexts/meta/processVaultData.ts | 12 +++--- .../contexts/meta/queryExtendedMetadata.ts | 14 +++---- .../src/contexts/meta/types.ts | 38 +++++++++---------- js/packages/common/src/utils/index.tsx | 1 + .../src/utils/isValidHttpUrl.ts | 0 js/packages/web/src/contexts/index.tsx | 2 +- js/packages/web/src/contexts/meta/index.ts | 1 - js/packages/web/src/providers.tsx | 2 +- 17 files changed, 85 insertions(+), 82 deletions(-) create mode 100644 js/packages/common/src/contexts/meta/index.ts rename js/packages/{web => common}/src/contexts/meta/isMetadataPartOfStore.ts (76%) rename js/packages/{web => common}/src/contexts/meta/loadAccounts.ts (97%) rename js/packages/{web => common}/src/contexts/meta/meta.tsx (97%) rename js/packages/{web => common}/src/contexts/meta/onChangeAccount.ts (100%) rename js/packages/{web => common}/src/contexts/meta/processAuctions.ts (95%) rename js/packages/{web => common}/src/contexts/meta/processMetaData.ts (95%) rename js/packages/{web => common}/src/contexts/meta/processMetaplexAccounts.ts (93%) rename js/packages/{web => common}/src/contexts/meta/processVaultData.ts (92%) rename js/packages/{web => common}/src/contexts/meta/queryExtendedMetadata.ts (94%) rename js/packages/{web => common}/src/contexts/meta/types.ts (94%) rename js/packages/{web => common}/src/utils/isValidHttpUrl.ts (100%) delete mode 100644 js/packages/web/src/contexts/meta/index.ts diff --git a/js/packages/common/src/contexts/index.tsx b/js/packages/common/src/contexts/index.tsx index 3842422..55bff1c 100644 --- a/js/packages/common/src/contexts/index.tsx +++ b/js/packages/common/src/contexts/index.tsx @@ -5,3 +5,4 @@ export * from './connection'; export * as Wallet from './wallet'; export * from './wallet'; export * from './store'; +export * from './meta'; diff --git a/js/packages/common/src/contexts/meta/index.ts b/js/packages/common/src/contexts/meta/index.ts new file mode 100644 index 0000000..1cc9367 --- /dev/null +++ b/js/packages/common/src/contexts/meta/index.ts @@ -0,0 +1,10 @@ +export * from './meta'; +export * from './isMetadataPartOfStore'; +export * from './loadAccounts'; +export * from './onChangeAccount'; +export * from './processAuctions'; +export * from './processMetaData'; +export * from './processMetaplexAccounts'; +export * from './processVaultData'; +export * from './queryExtendedMetadata'; +export * from './types'; diff --git a/js/packages/web/src/contexts/meta/isMetadataPartOfStore.ts b/js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts similarity index 76% rename from js/packages/web/src/contexts/meta/isMetadataPartOfStore.ts rename to js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts index 562508f..310a59a 100644 --- a/js/packages/web/src/contexts/meta/isMetadataPartOfStore.ts +++ b/js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts @@ -1,8 +1,6 @@ -import { Metadata, ParsedAccount } from '@oyster/common'; -import { - Store, - WhitelistedCreator, -} from '@oyster/common/dist/lib/models/metaplex/index'; +import { Metadata } from '../../actions'; +import { Store, WhitelistedCreator } from '../../models/metaplex'; +import { ParsedAccount } from '../accounts'; export const isMetadataPartOfStore = ( m: ParsedAccount, diff --git a/js/packages/web/src/contexts/meta/loadAccounts.ts b/js/packages/common/src/contexts/meta/loadAccounts.ts similarity index 97% rename from js/packages/web/src/contexts/meta/loadAccounts.ts rename to js/packages/common/src/contexts/meta/loadAccounts.ts index f73382f..0d37c84 100644 --- a/js/packages/web/src/contexts/meta/loadAccounts.ts +++ b/js/packages/common/src/contexts/meta/loadAccounts.ts @@ -5,18 +5,18 @@ import { StringPublicKey, toPublicKey, VAULT_ID, -} from '@oyster/common/dist/lib/utils/ids'; -import { getMultipleAccounts, Metadata, ParsedAccount } from '@oyster/common'; -import { MAX_WHITELISTED_CREATOR_SIZE } from '@oyster/common/dist/lib/models/index'; +} from '../../utils/ids'; +import { MAX_WHITELISTED_CREATOR_SIZE } from '../../models'; import { getEdition, + Metadata, MAX_CREATOR_LEN, MAX_CREATOR_LIMIT, MAX_NAME_LENGTH, MAX_SYMBOL_LENGTH, MAX_URI_LENGTH, METADATA_PREFIX, -} from '@oyster/common/dist/lib/actions/index'; +} from '../../actions'; import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'; import { AccountAndPubkey, MetaState, ProcessAccountsFunc } from './types'; import { isMetadataPartOfStore } from './isMetadataPartOfStore'; @@ -24,6 +24,7 @@ import { processAuctions } from './processAuctions'; import { processMetaplexAccounts } from './processMetaplexAccounts'; import { processMetaData } from './processMetaData'; import { processVaultData } from './processVaultData'; +import { ParsedAccount, getMultipleAccounts } from '../accounts'; async function getProgramAccounts( connection: Connection, diff --git a/js/packages/web/src/contexts/meta/meta.tsx b/js/packages/common/src/contexts/meta/meta.tsx similarity index 97% rename from js/packages/web/src/contexts/meta/meta.tsx rename to js/packages/common/src/contexts/meta/meta.tsx index fe98208..c243f7e 100644 --- a/js/packages/web/src/contexts/meta/meta.tsx +++ b/js/packages/common/src/contexts/meta/meta.tsx @@ -1,13 +1,3 @@ -import { - useConnection, - useStore, - AUCTION_ID, - METAPLEX_ID, - VAULT_ID, - METADATA_PROGRAM_ID, - toPublicKey, - useQuerySearch, -} from '@oyster/common'; import React, { useCallback, useContext, @@ -27,6 +17,16 @@ import { metadataByMintUpdater, } from './loadAccounts'; import { onChangeAccount } from './onChangeAccount'; +import { useConnection } from '../connection'; +import { + AUCTION_ID, + METADATA_PROGRAM_ID, + METAPLEX_ID, + toPublicKey, + VAULT_ID, +} from '../../utils'; +import { useQuerySearch } from '../../hooks'; +import { useStore } from '../store'; const MetaContext = React.createContext({ metadata: [], diff --git a/js/packages/web/src/contexts/meta/onChangeAccount.ts b/js/packages/common/src/contexts/meta/onChangeAccount.ts similarity index 100% rename from js/packages/web/src/contexts/meta/onChangeAccount.ts rename to js/packages/common/src/contexts/meta/onChangeAccount.ts diff --git a/js/packages/web/src/contexts/meta/processAuctions.ts b/js/packages/common/src/contexts/meta/processAuctions.ts similarity index 95% rename from js/packages/web/src/contexts/meta/processAuctions.ts rename to js/packages/common/src/contexts/meta/processAuctions.ts index 8bbb308..701eb82 100644 --- a/js/packages/web/src/contexts/meta/processAuctions.ts +++ b/js/packages/common/src/contexts/meta/processAuctions.ts @@ -1,19 +1,18 @@ import { - AuctionParser, - cache, - ParsedAccount, AuctionData, + AuctionDataExtended, + AuctionDataExtendedParser, + AuctionParser, BidderMetadata, BidderMetadataParser, BidderPot, BidderPotParser, BIDDER_METADATA_LEN, BIDDER_POT_LEN, - AuctionDataExtended, MAX_AUCTION_DATA_EXTENDED_SIZE, - AuctionDataExtendedParser, - AUCTION_ID, -} from '@oyster/common'; +} from '../../actions'; +import { AUCTION_ID } from '../../utils'; +import { cache, ParsedAccount } from '../accounts'; import { CheckAccountFunc, ProcessAccountsFunc } from './types'; export const processAuctions: ProcessAccountsFunc = ( diff --git a/js/packages/web/src/contexts/meta/processMetaData.ts b/js/packages/common/src/contexts/meta/processMetaData.ts similarity index 95% rename from js/packages/web/src/contexts/meta/processMetaData.ts rename to js/packages/common/src/contexts/meta/processMetaData.ts index 4bce00b..7570000 100644 --- a/js/packages/web/src/contexts/meta/processMetaData.ts +++ b/js/packages/common/src/contexts/meta/processMetaData.ts @@ -1,18 +1,18 @@ -import { - decodeMetadata, - decodeEdition, - decodeMasterEdition, - Metadata, - ParsedAccount, - Edition, - MasterEditionV1, - MasterEditionV2, - MetadataKey, - METADATA_PROGRAM_ID, -} from '@oyster/common'; import { AccountInfo } from '@solana/web3.js'; import { ProcessAccountsFunc } from './types'; import { isValidHttpUrl } from '../../utils/isValidHttpUrl'; +import { + decodeEdition, + decodeMasterEdition, + decodeMetadata, + Edition, + MasterEditionV1, + MasterEditionV2, + Metadata, + MetadataKey, +} from '../../actions'; +import { ParsedAccount } from '../accounts'; +import { METADATA_PROGRAM_ID } from '../../utils'; export const processMetaData: ProcessAccountsFunc = ( { account, pubkey }, diff --git a/js/packages/web/src/contexts/meta/processMetaplexAccounts.ts b/js/packages/common/src/contexts/meta/processMetaplexAccounts.ts similarity index 93% rename from js/packages/web/src/contexts/meta/processMetaplexAccounts.ts rename to js/packages/common/src/contexts/meta/processMetaplexAccounts.ts index c4fb51f..3eabb70 100644 --- a/js/packages/web/src/contexts/meta/processMetaplexAccounts.ts +++ b/js/packages/common/src/contexts/meta/processMetaplexAccounts.ts @@ -1,4 +1,3 @@ -import { programIds, cache, ParsedAccount, METAPLEX_ID } from '@oyster/common'; import { AccountInfo, PublicKey } from '@solana/web3.js'; import { AuctionManagerV1, @@ -20,9 +19,10 @@ import { BidRedemptionTicketV2, decodeSafetyDepositConfig, SafetyDepositConfig, -} from '@oyster/common/dist/lib/models/metaplex/index'; -import names from '../../config/userNames.json'; +} from '../../models/metaplex'; import { ProcessAccountsFunc } from './types'; +import { METAPLEX_ID, programIds } from '../../utils'; +import { cache, ParsedAccount } from '../accounts'; export const processMetaplexAccounts: ProcessAccountsFunc = async ( { account, pubkey }, @@ -145,12 +145,6 @@ export const processMetaplexAccounts: ProcessAccountsFunc = async ( false, ) as ParsedAccount; - const nameInfo = (names as any)[parsedAccount.info.address]; - - if (nameInfo) { - parsedAccount.info = { ...parsedAccount.info, ...nameInfo }; - } - setter( 'whitelistedCreatorsByCreator', whitelistedCreator.address, diff --git a/js/packages/web/src/contexts/meta/processVaultData.ts b/js/packages/common/src/contexts/meta/processVaultData.ts similarity index 92% rename from js/packages/web/src/contexts/meta/processVaultData.ts rename to js/packages/common/src/contexts/meta/processVaultData.ts index 0a7f4cf..af51412 100644 --- a/js/packages/web/src/contexts/meta/processVaultData.ts +++ b/js/packages/common/src/contexts/meta/processVaultData.ts @@ -1,13 +1,13 @@ +import { AccountInfo } from '@solana/web3.js'; import { - ParsedAccount, - SafetyDepositBox, - VaultKey, decodeSafetyDeposit, decodeVault, + SafetyDepositBox, Vault, -} from '@oyster/common'; -import { VAULT_ID } from '@oyster/common/dist/lib/utils/ids'; -import { AccountInfo } from '@solana/web3.js'; + VaultKey, +} from '../../actions'; +import { VAULT_ID } from '../../utils'; +import { ParsedAccount } from '../accounts'; import { ProcessAccountsFunc } from './types'; export const processVaultData: ProcessAccountsFunc = ( diff --git a/js/packages/web/src/contexts/meta/queryExtendedMetadata.ts b/js/packages/common/src/contexts/meta/queryExtendedMetadata.ts similarity index 94% rename from js/packages/web/src/contexts/meta/queryExtendedMetadata.ts rename to js/packages/common/src/contexts/meta/queryExtendedMetadata.ts index 4c1b7f3..1f763b6 100644 --- a/js/packages/web/src/contexts/meta/queryExtendedMetadata.ts +++ b/js/packages/common/src/contexts/meta/queryExtendedMetadata.ts @@ -1,12 +1,12 @@ -import { - Metadata, - getMultipleAccounts, - cache, - MintParser, - ParsedAccount, -} from '@oyster/common'; import { MintInfo } from '@solana/spl-token'; import { Connection } from '@solana/web3.js'; +import { Metadata } from '../../actions'; +import { + cache, + getMultipleAccounts, + MintParser, + ParsedAccount, +} from '../accounts'; export const queryExtendedMetadata = async ( connection: Connection, diff --git a/js/packages/web/src/contexts/meta/types.ts b/js/packages/common/src/contexts/meta/types.ts similarity index 94% rename from js/packages/web/src/contexts/meta/types.ts rename to js/packages/common/src/contexts/meta/types.ts index 00f6928..b4d3109 100644 --- a/js/packages/web/src/contexts/meta/types.ts +++ b/js/packages/common/src/contexts/meta/types.ts @@ -1,29 +1,29 @@ -import { - Metadata, - ParsedAccount, - Edition, - AuctionData, - SafetyDepositBox, - BidderMetadata, - BidderPot, - Vault, - AuctionDataExtended, - MasterEditionV1, - MasterEditionV2, - PublicKeyStringAndAccount, -} from '@oyster/common'; import { AccountInfo } from '@solana/web3.js'; +import { + AuctionData, + AuctionDataExtended, + BidderMetadata, + BidderPot, + Edition, + MasterEditionV1, + MasterEditionV2, + Metadata, + SafetyDepositBox, + Vault, +} from '../../actions'; import { AuctionManagerV1, + AuctionManagerV2, BidRedemptionTicket, - Store, - WhitelistedCreator, + BidRedemptionTicketV2, PayoutTicket, PrizeTrackingTicket, - AuctionManagerV2, SafetyDepositConfig, - BidRedemptionTicketV2, -} from '@oyster/common/dist/lib/models/metaplex/index'; + Store, + WhitelistedCreator, +} from '../../models/metaplex'; +import { ParsedAccount } from '../accounts'; +import { PublicKeyStringAndAccount } from '../../utils'; export interface MetaState { metadata: ParsedAccount[]; diff --git a/js/packages/common/src/utils/index.tsx b/js/packages/common/src/utils/index.tsx index 82a9c93..ea95b4e 100644 --- a/js/packages/common/src/utils/index.tsx +++ b/js/packages/common/src/utils/index.tsx @@ -6,4 +6,5 @@ export * from './notifications'; export * from './utils'; export * from './strings'; export * as shortvec from './shortvec'; +export * from './isValidHttpUrl'; export * from './borsh'; diff --git a/js/packages/web/src/utils/isValidHttpUrl.ts b/js/packages/common/src/utils/isValidHttpUrl.ts similarity index 100% rename from js/packages/web/src/utils/isValidHttpUrl.ts rename to js/packages/common/src/utils/isValidHttpUrl.ts diff --git a/js/packages/web/src/contexts/index.tsx b/js/packages/web/src/contexts/index.tsx index 59ec44d..ea3d305 100644 --- a/js/packages/web/src/contexts/index.tsx +++ b/js/packages/web/src/contexts/index.tsx @@ -1,2 +1,2 @@ -export * from './meta'; +export * from '@oyster/common/dist/lib/contexts/meta/meta' export * from './coingecko'; diff --git a/js/packages/web/src/contexts/meta/index.ts b/js/packages/web/src/contexts/meta/index.ts deleted file mode 100644 index c9942f4..0000000 --- a/js/packages/web/src/contexts/meta/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './meta'; diff --git a/js/packages/web/src/providers.tsx b/js/packages/web/src/providers.tsx index 437c376..19a987f 100644 --- a/js/packages/web/src/providers.tsx +++ b/js/packages/web/src/providers.tsx @@ -3,12 +3,12 @@ import { ConnectionProvider, StoreProvider, WalletProvider, + MetaProvider, } from '@oyster/common'; import { FC } from 'react'; import { UseWalletProvider } from 'use-wallet'; import { ConfettiProvider } from './components/Confetti'; import { AppLayout } from './components/Layout'; -import { MetaProvider } from './contexts/meta'; import { CoingeckoProvider } from './contexts/coingecko'; export const Providers: FC = ({ children }) => { From 65b885c9dd0330852a1e8fef397ee961a4e1673d Mon Sep 17 00:00:00 2001 From: exromany Date: Mon, 16 Aug 2021 12:58:34 +0300 Subject: [PATCH 03/10] split contexts/accounts --- js/packages/common/src/actions/account.ts | 3 +- js/packages/common/src/actions/auction.ts | 2 +- js/packages/common/src/contexts/accounts.tsx | 664 ------------------ .../common/src/contexts/accounts/accounts.tsx | 286 ++++++++ .../common/src/contexts/accounts/cache.ts | 193 +++++ .../src/contexts/accounts/deserialize.ts | 63 ++ .../contexts/accounts/getMultipleAccounts.ts | 56 ++ .../common/src/contexts/accounts/index.ts | 6 + .../common/src/contexts/accounts/parsesrs.ts | 64 ++ .../common/src/contexts/accounts/types.ts | 17 + .../contexts/meta/isMetadataPartOfStore.ts | 2 +- .../common/src/contexts/meta/loadAccounts.ts | 3 +- .../src/contexts/meta/processAuctions.ts | 3 +- .../src/contexts/meta/processMetaData.ts | 2 +- .../contexts/meta/processMetaplexAccounts.ts | 3 +- .../src/contexts/meta/processVaultData.ts | 2 +- .../contexts/meta/queryExtendedMetadata.ts | 10 +- js/packages/common/src/contexts/meta/types.ts | 2 +- js/packages/common/src/utils/ids.ts | 4 + js/packages/web/src/contexts/index.tsx | 2 +- js/packages/web/src/views/admin/index.tsx | 5 +- js/packages/web/src/views/analytics/index.tsx | 6 +- 22 files changed, 716 insertions(+), 682 deletions(-) delete mode 100644 js/packages/common/src/contexts/accounts.tsx create mode 100644 js/packages/common/src/contexts/accounts/accounts.tsx create mode 100644 js/packages/common/src/contexts/accounts/cache.ts create mode 100644 js/packages/common/src/contexts/accounts/deserialize.ts create mode 100644 js/packages/common/src/contexts/accounts/getMultipleAccounts.ts create mode 100644 js/packages/common/src/contexts/accounts/index.ts create mode 100644 js/packages/common/src/contexts/accounts/parsesrs.ts create mode 100644 js/packages/common/src/contexts/accounts/types.ts diff --git a/js/packages/common/src/actions/account.ts b/js/packages/common/src/actions/account.ts index 4ce7069..133222d 100644 --- a/js/packages/common/src/actions/account.ts +++ b/js/packages/common/src/actions/account.ts @@ -13,7 +13,8 @@ import { } from '../utils/ids'; import { programIds } from '../utils/programIds'; import { TokenAccount } from '../models/account'; -import { cache, TokenAccountParser } from '../contexts/accounts'; +import { cache } from '../contexts/accounts/cache'; +import { TokenAccountParser } from '../contexts/accounts/parsesrs'; export function ensureSplAccount( instructions: TransactionInstruction[], diff --git a/js/packages/common/src/actions/auction.ts b/js/packages/common/src/actions/auction.ts index abab2ed..793529a 100644 --- a/js/packages/common/src/actions/auction.ts +++ b/js/packages/common/src/actions/auction.ts @@ -8,7 +8,7 @@ import { import { programIds } from '../utils/programIds'; import { deserializeUnchecked, serialize } from 'borsh'; import BN from 'bn.js'; -import { AccountParser } from '../contexts'; +import { AccountParser } from '../contexts/accounts/types'; import moment from 'moment'; import { findProgramAddress, StringPublicKey, toPublicKey } from '../utils'; export const AUCTION_PREFIX = 'auction'; diff --git a/js/packages/common/src/contexts/accounts.tsx b/js/packages/common/src/contexts/accounts.tsx deleted file mode 100644 index b6b08a7..0000000 --- a/js/packages/common/src/contexts/accounts.tsx +++ /dev/null @@ -1,664 +0,0 @@ -import React, { - useCallback, - useContext, - useEffect, - useMemo, - useState, -} from 'react'; -import { useConnection } from '../contexts/connection'; -import { useWallet } from '@solana/wallet-adapter-react'; -import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'; -import { AccountLayout, MintInfo, MintLayout, u64 } from '@solana/spl-token'; -import { TokenAccount } from '../models'; -import { chunks } from '../utils/utils'; -import { EventEmitter } from '../utils/eventEmitter'; -import { StringPublicKey, WRAPPED_SOL_MINT } from '../utils/ids'; -import { programIds } from '../utils/programIds'; - -const AccountsContext = React.createContext(null); - -const pendingCalls = new Map>(); -const genericCache = new Map(); -const pendingMintCalls = new Map>(); -const mintCache = new Map(); - -export interface ParsedAccountBase { - pubkey: StringPublicKey; - account: AccountInfo; - info: any; // TODO: change to unknown -} - -export type AccountParser = ( - pubkey: StringPublicKey, - data: AccountInfo, -) => ParsedAccountBase | undefined; - -export interface ParsedAccount extends ParsedAccountBase { - info: T; -} - -const getMintInfo = async (connection: Connection, pubKey: PublicKey) => { - const info = await connection.getAccountInfo(pubKey); - if (info === null) { - throw new Error('Failed to find mint account'); - } - - const data = Buffer.from(info.data); - - return deserializeMint(data); -}; - -export const MintParser = (pubKey: string, info: AccountInfo) => { - const buffer = Buffer.from(info.data); - - const data = deserializeMint(buffer); - - const details = { - pubkey: pubKey, - account: { - ...info, - }, - info: data, - } as ParsedAccountBase; - - return details; -}; - -export const TokenAccountParser = ( - pubKey: string, - info: AccountInfo, -) => { - // Sometimes a wrapped sol account gets closed, goes to 0 length, - // triggers an update over wss which triggers this guy to get called - // since your UI already logged that pubkey as a token account. Check for length. - if (info.data.length > 0) { - const buffer = Buffer.from(info.data); - const data = deserializeAccount(buffer); - - const details = { - pubkey: pubKey, - account: { - ...info, - }, - info: data, - } as TokenAccount; - - return details; - } -}; - -export const GenericAccountParser = ( - pubKey: string, - info: AccountInfo, -) => { - const buffer = Buffer.from(info.data); - - const details = { - pubkey: pubKey, - account: { - ...info, - }, - info: buffer, - } as ParsedAccountBase; - - return details; -}; - -export const keyToAccountParser = new Map(); - -export const cache = { - emitter: new EventEmitter(), - query: async ( - connection: Connection, - pubKey: string | PublicKey, - parser?: AccountParser, - ) => { - let id: PublicKey; - if (typeof pubKey === 'string') { - id = new PublicKey(pubKey); - } else { - id = pubKey; - } - - const address = id.toBase58(); - - let account = genericCache.get(address); - if (account) { - return account; - } - - let query = pendingCalls.get(address); - if (query) { - return query; - } - - // TODO: refactor to use multiple accounts query with flush like behavior - query = connection.getAccountInfo(id).then(data => { - if (!data) { - throw new Error('Account not found'); - } - - return cache.add(id, data, parser); - }) as Promise; - pendingCalls.set(address, query as any); - - return query; - }, - add: ( - id: PublicKey | string, - obj: AccountInfo, - parser?: AccountParser, - isActive?: boolean | undefined | ((parsed: any) => boolean), - ) => { - const address = typeof id === 'string' ? id : id?.toBase58(); - const deserialize = parser ? parser : keyToAccountParser.get(address); - if (!deserialize) { - throw new Error( - 'Deserializer needs to be registered or passed as a parameter', - ); - } - - cache.registerParser(id, deserialize); - pendingCalls.delete(address); - const account = deserialize(address, obj); - if (!account) { - return; - } - - if (isActive === undefined) isActive = true; - else if (isActive instanceof Function) isActive = isActive(account); - - const isNew = !genericCache.has(address); - - genericCache.set(address, account); - cache.emitter.raiseCacheUpdated(address, isNew, deserialize, isActive); - return account; - }, - get: (pubKey: string | PublicKey) => { - let key: string; - if (typeof pubKey !== 'string') { - key = pubKey.toBase58(); - } else { - key = pubKey; - } - - return genericCache.get(key); - }, - delete: (pubKey: string | PublicKey) => { - let key: string; - if (typeof pubKey !== 'string') { - key = pubKey.toBase58(); - } else { - key = pubKey; - } - - if (genericCache.get(key)) { - genericCache.delete(key); - cache.emitter.raiseCacheDeleted(key); - return true; - } - return false; - }, - - byParser: (parser: AccountParser) => { - const result: string[] = []; - for (const id of keyToAccountParser.keys()) { - if (keyToAccountParser.get(id) === parser) { - result.push(id); - } - } - - return result; - }, - registerParser: (pubkey: PublicKey | string, parser: AccountParser) => { - if (pubkey) { - const address = typeof pubkey === 'string' ? pubkey : pubkey?.toBase58(); - keyToAccountParser.set(address, parser); - } - - return pubkey; - }, - queryMint: async (connection: Connection, pubKey: string | PublicKey) => { - let id: PublicKey; - if (typeof pubKey === 'string') { - id = new PublicKey(pubKey); - } else { - id = pubKey; - } - - const address = id.toBase58(); - let mint = mintCache.get(address); - if (mint) { - return mint; - } - - let query = pendingMintCalls.get(address); - if (query) { - return query; - } - - query = getMintInfo(connection, id).then(data => { - pendingMintCalls.delete(address); - - mintCache.set(address, data); - return data; - }) as Promise; - pendingMintCalls.set(address, query as any); - - return query; - }, - getMint: (pubKey: string | PublicKey) => { - let key: string; - if (typeof pubKey !== 'string') { - key = pubKey.toBase58(); - } else { - key = pubKey; - } - - return mintCache.get(key); - }, - addMint: (pubKey: PublicKey, obj: AccountInfo) => { - const mint = deserializeMint(obj.data); - const id = pubKey.toBase58(); - mintCache.set(id, mint); - return mint; - }, -}; - -export const useAccountsContext = () => { - const context = useContext(AccountsContext); - - return context; -}; - -function wrapNativeAccount( - pubkey: string, - account?: AccountInfo, -): TokenAccount | undefined { - if (!account) { - return undefined; - } - - const key = new PublicKey(pubkey); - - return { - pubkey: pubkey, - account, - info: { - address: key, - mint: WRAPPED_SOL_MINT, - owner: key, - amount: new u64(account.lamports), - delegate: null, - delegatedAmount: new u64(0), - isInitialized: true, - isFrozen: false, - isNative: true, - rentExemptReserve: null, - closeAuthority: null, - }, - }; -} - -export const getCachedAccount = ( - predicate: (account: TokenAccount) => boolean, -) => { - for (const account of genericCache.values()) { - if (predicate(account)) { - return account as TokenAccount; - } - } -}; - -const UseNativeAccount = () => { - const connection = useConnection(); - const { publicKey } = useWallet(); - - const [nativeAccount, setNativeAccount] = useState>(); - - const updateCache = useCallback( - account => { - if (publicKey) { - const wrapped = wrapNativeAccount(publicKey.toBase58(), account); - if (wrapped !== undefined) { - const id = publicKey.toBase58(); - cache.registerParser(id, TokenAccountParser); - genericCache.set(id, wrapped as TokenAccount); - cache.emitter.raiseCacheUpdated(id, false, TokenAccountParser, true); - } - } - }, - [publicKey], - ); - - useEffect(() => { - let subId = 0; - const updateAccount = (account: AccountInfo | null) => { - if (account) { - updateCache(account); - setNativeAccount(account); - } - }; - - (async () => { - if (!connection || !publicKey) { - return; - } - - const account = await connection.getAccountInfo(publicKey); - updateAccount(account); - - subId = connection.onAccountChange(publicKey, updateAccount); - })(); - - return () => { - if (subId) { - connection.removeAccountChangeListener(subId); - } - }; - }, [setNativeAccount, publicKey, connection, updateCache]); - - return { nativeAccount }; -}; - -const PRECACHED_OWNERS = new Set(); -const precacheUserTokenAccounts = async ( - connection: Connection, - owner?: PublicKey, -) => { - if (!owner) { - return; - } - - // used for filtering account updates over websocket - PRECACHED_OWNERS.add(owner.toBase58()); - - // user accounts are updated via ws subscription - const accounts = await connection.getTokenAccountsByOwner(owner, { - programId: programIds().token, - }); - - accounts.value.forEach(info => { - cache.add(info.pubkey.toBase58(), info.account, TokenAccountParser); - }); -}; - -export function AccountsProvider({ children = null as any }) { - const connection = useConnection(); - const { publicKey } = useWallet(); - const [tokenAccounts, setTokenAccounts] = useState([]); - const [userAccounts, setUserAccounts] = useState([]); - const { nativeAccount } = UseNativeAccount(); - const walletKey = publicKey?.toBase58(); - - const selectUserAccounts = useCallback(() => { - return cache - .byParser(TokenAccountParser) - .map(id => cache.get(id)) - .filter(a => a && a.info.owner.toBase58() === walletKey) - .map(a => a as TokenAccount); - }, [walletKey]); - - useEffect(() => { - const accounts = selectUserAccounts().filter( - a => a !== undefined, - ) as TokenAccount[]; - setUserAccounts(accounts); - }, [nativeAccount, tokenAccounts, selectUserAccounts]); - - useEffect(() => { - const subs: number[] = []; - cache.emitter.onCache(args => { - if (args.isNew && args.isActive) { - let id = args.id; - let deserialize = args.parser; - connection.onAccountChange(new PublicKey(id), info => { - cache.add(id, info, deserialize); - }); - } - }); - - return () => { - subs.forEach(id => connection.removeAccountChangeListener(id)); - }; - }, [connection]); - - useEffect(() => { - if (!connection || !publicKey) { - setTokenAccounts([]); - } else { - precacheUserTokenAccounts(connection, publicKey).then(() => { - setTokenAccounts(selectUserAccounts()); - }); - - // This can return different types of accounts: token-account, mint, multisig - // TODO: web3.js expose ability to filter. - // this should use only filter syntax to only get accounts that are owned by user - const tokenSubID = connection.onProgramAccountChange( - programIds().token, - info => { - // TODO: fix type in web3.js - const id = info.accountId as unknown as string; - // TODO: do we need a better way to identify layout (maybe a enum identifing type?) - if (info.accountInfo.data.length === AccountLayout.span) { - const data = deserializeAccount(info.accountInfo.data); - - if (PRECACHED_OWNERS.has(data.owner.toBase58())) { - cache.add(id, info.accountInfo, TokenAccountParser); - setTokenAccounts(selectUserAccounts()); - } - } - }, - 'singleGossip', - ); - - return () => { - connection.removeProgramAccountChangeListener(tokenSubID); - }; - } - }, [connection, publicKey, selectUserAccounts]); - - return ( - - {children} - - ); -} - -export function useNativeAccount() { - const context = useContext(AccountsContext); - return { - account: context.nativeAccount as AccountInfo, - }; -} - -export const getMultipleAccounts = async ( - connection: any, - keys: string[], - commitment: string, -) => { - const result = await Promise.all( - chunks(keys, 99).map(chunk => - getMultipleAccountsCore(connection, chunk, commitment), - ), - ); - - const array = result - .map( - a => - a.array.map(acc => { - if (!acc) { - return undefined; - } - - const { data, ...rest } = acc; - const obj = { - ...rest, - data: Buffer.from(data[0], 'base64'), - } as AccountInfo; - return obj; - }) as AccountInfo[], - ) - .flat(); - return { keys, array }; -}; - -const getMultipleAccountsCore = async ( - connection: any, - keys: string[], - commitment: string, -) => { - const args = connection._buildArgs([keys], commitment, 'base64'); - - const unsafeRes = await connection._rpcRequest('getMultipleAccounts', args); - if (unsafeRes.error) { - throw new Error( - 'failed to get info about account ' + unsafeRes.error.message, - ); - } - - if (unsafeRes.result.value) { - const array = unsafeRes.result.value as AccountInfo[]; - return { keys, array }; - } - - // TODO: fix - throw new Error(); -}; - -export function useMint(key?: string | PublicKey) { - const connection = useConnection(); - const [mint, setMint] = useState(); - - const id = typeof key === 'string' ? key : key?.toBase58(); - - useEffect(() => { - if (!id) { - return; - } - - cache - .query(connection, id, MintParser) - .then(acc => setMint(acc.info as any)) - .catch(err => console.log(err)); - - const dispose = cache.emitter.onCache(e => { - const event = e; - if (event.id === id) { - cache - .query(connection, id, MintParser) - .then(mint => setMint(mint.info as any)); - } - }); - return () => { - dispose(); - }; - }, [connection, id]); - - return mint; -} - -export function useAccount(pubKey?: PublicKey) { - const connection = useConnection(); - const [account, setAccount] = useState(); - - const key = pubKey?.toBase58(); - useEffect(() => { - const query = async () => { - try { - if (!key) { - return; - } - - const acc = await cache - .query(connection, key, TokenAccountParser) - .catch(err => console.log(err)); - if (acc) { - setAccount(acc); - } - } catch (err) { - console.error(err); - } - }; - - query(); - - const dispose = cache.emitter.onCache(e => { - const event = e; - if (event.id === key) { - query(); - } - }); - return () => { - dispose(); - }; - }, [connection, key]); - - return account; -} - -// TODO: expose in spl package -export const deserializeAccount = (data: Buffer) => { - const accountInfo = AccountLayout.decode(data); - accountInfo.mint = new PublicKey(accountInfo.mint); - accountInfo.owner = new PublicKey(accountInfo.owner); - accountInfo.amount = u64.fromBuffer(accountInfo.amount); - - if (accountInfo.delegateOption === 0) { - accountInfo.delegate = null; - accountInfo.delegatedAmount = new u64(0); - } else { - accountInfo.delegate = new PublicKey(accountInfo.delegate); - accountInfo.delegatedAmount = u64.fromBuffer(accountInfo.delegatedAmount); - } - - accountInfo.isInitialized = accountInfo.state !== 0; - accountInfo.isFrozen = accountInfo.state === 2; - - if (accountInfo.isNativeOption === 1) { - accountInfo.rentExemptReserve = u64.fromBuffer(accountInfo.isNative); - accountInfo.isNative = true; - } else { - accountInfo.rentExemptReserve = null; - accountInfo.isNative = false; - } - - if (accountInfo.closeAuthorityOption === 0) { - accountInfo.closeAuthority = null; - } else { - accountInfo.closeAuthority = new PublicKey(accountInfo.closeAuthority); - } - - return accountInfo; -}; - -// TODO: expose in spl package -export const deserializeMint = (data: Buffer) => { - if (data.length !== MintLayout.span) { - throw new Error('Not a valid Mint'); - } - - const mintInfo = MintLayout.decode(data); - - if (mintInfo.mintAuthorityOption === 0) { - mintInfo.mintAuthority = null; - } else { - mintInfo.mintAuthority = new PublicKey(mintInfo.mintAuthority); - } - - mintInfo.supply = u64.fromBuffer(mintInfo.supply); - mintInfo.isInitialized = mintInfo.isInitialized !== 0; - - if (mintInfo.freezeAuthorityOption === 0) { - mintInfo.freezeAuthority = null; - } else { - mintInfo.freezeAuthority = new PublicKey(mintInfo.freezeAuthority); - } - - return mintInfo as MintInfo; -}; diff --git a/js/packages/common/src/contexts/accounts/accounts.tsx b/js/packages/common/src/contexts/accounts/accounts.tsx new file mode 100644 index 0000000..656f85d --- /dev/null +++ b/js/packages/common/src/contexts/accounts/accounts.tsx @@ -0,0 +1,286 @@ +import React, { useCallback, useContext, useEffect, useState } from 'react'; +import { useWallet } from '@solana/wallet-adapter-react'; +import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'; +import { AccountLayout, MintInfo, u64 } from '@solana/spl-token'; +import { useConnection } from '../../contexts/connection'; +import { TokenAccount } from '../../models'; +import { StringPublicKey, WRAPPED_SOL_MINT } from '../../utils/ids'; +import { programIds } from '../../utils/programIds'; +import { genericCache, cache } from './cache'; +import { deserializeAccount } from './deserialize'; +import { TokenAccountParser, MintParser } from './parsesrs'; + +const AccountsContext = React.createContext(null); + +export const useAccountsContext = () => { + const context = useContext(AccountsContext); + + return context; +}; + +function wrapNativeAccount( + pubkey: StringPublicKey, + account?: AccountInfo, +): TokenAccount | undefined { + if (!account) { + return undefined; + } + + const key = new PublicKey(pubkey); + + return { + pubkey: pubkey, + account, + info: { + address: key, + mint: WRAPPED_SOL_MINT, + owner: key, + amount: new u64(account.lamports), + delegate: null, + delegatedAmount: new u64(0), + isInitialized: true, + isFrozen: false, + isNative: true, + rentExemptReserve: null, + closeAuthority: null, + }, + }; +} + +const UseNativeAccount = () => { + const connection = useConnection(); + const { publicKey } = useWallet(); + + const [nativeAccount, setNativeAccount] = useState>(); + + const updateCache = useCallback( + account => { + if (publicKey) { + const wrapped = wrapNativeAccount(publicKey.toBase58(), account); + if (wrapped !== undefined) { + const id = publicKey.toBase58(); + cache.registerParser(id, TokenAccountParser); + genericCache.set(id, wrapped as TokenAccount); + cache.emitter.raiseCacheUpdated(id, false, TokenAccountParser, true); + } + } + }, + [publicKey], + ); + + useEffect(() => { + let subId = 0; + const updateAccount = (account: AccountInfo | null) => { + if (account) { + updateCache(account); + setNativeAccount(account); + } + }; + + (async () => { + if (!connection || !publicKey) { + return; + } + + const account = await connection.getAccountInfo(publicKey); + updateAccount(account); + + subId = connection.onAccountChange(publicKey, updateAccount); + })(); + + return () => { + if (subId) { + connection.removeAccountChangeListener(subId); + } + }; + }, [setNativeAccount, publicKey, connection, updateCache]); + + return { nativeAccount }; +}; + +const PRECACHED_OWNERS = new Set(); +const precacheUserTokenAccounts = async ( + connection: Connection, + owner?: PublicKey, +) => { + if (!owner) { + return; + } + + // used for filtering account updates over websocket + PRECACHED_OWNERS.add(owner.toBase58()); + + // user accounts are updated via ws subscription + const accounts = await connection.getTokenAccountsByOwner(owner, { + programId: programIds().token, + }); + + accounts.value.forEach(info => { + cache.add(info.pubkey.toBase58(), info.account, TokenAccountParser); + }); +}; + +export function AccountsProvider({ children = null as any }) { + const connection = useConnection(); + const { publicKey } = useWallet(); + const [tokenAccounts, setTokenAccounts] = useState([]); + const [userAccounts, setUserAccounts] = useState([]); + const { nativeAccount } = UseNativeAccount(); + const walletKey = publicKey?.toBase58(); + + const selectUserAccounts = useCallback(() => { + return cache + .byParser(TokenAccountParser) + .map(id => cache.get(id)) + .filter(a => a && a.info.owner.toBase58() === walletKey) + .map(a => a as TokenAccount); + }, [walletKey]); + + useEffect(() => { + const accounts = selectUserAccounts().filter( + a => a !== undefined, + ) as TokenAccount[]; + setUserAccounts(accounts); + }, [nativeAccount, tokenAccounts, selectUserAccounts]); + + useEffect(() => { + const subs: number[] = []; + cache.emitter.onCache(args => { + if (args.isNew && args.isActive) { + let id = args.id; + let deserialize = args.parser; + connection.onAccountChange(new PublicKey(id), info => { + cache.add(id, info, deserialize); + }); + } + }); + + return () => { + subs.forEach(id => connection.removeAccountChangeListener(id)); + }; + }, [connection]); + + useEffect(() => { + if (!connection || !publicKey) { + setTokenAccounts([]); + } else { + precacheUserTokenAccounts(connection, publicKey).then(() => { + setTokenAccounts(selectUserAccounts()); + }); + + // This can return different types of accounts: token-account, mint, multisig + // TODO: web3.js expose ability to filter. + // this should use only filter syntax to only get accounts that are owned by user + const tokenSubID = connection.onProgramAccountChange( + programIds().token, + info => { + // TODO: fix type in web3.js + const id = info.accountId as unknown as string; + // TODO: do we need a better way to identify layout (maybe a enum identifing type?) + if (info.accountInfo.data.length === AccountLayout.span) { + const data = deserializeAccount(info.accountInfo.data); + + if (PRECACHED_OWNERS.has(data.owner.toBase58())) { + cache.add(id, info.accountInfo, TokenAccountParser); + setTokenAccounts(selectUserAccounts()); + } + } + }, + 'singleGossip', + ); + + return () => { + connection.removeProgramAccountChangeListener(tokenSubID); + }; + } + }, [connection, publicKey, selectUserAccounts]); + + return ( + + {children} + + ); +} + +export function useNativeAccount() { + const context = useContext(AccountsContext); + return { + account: context.nativeAccount as AccountInfo, + }; +} + +export function useMint(key?: string | PublicKey) { + const connection = useConnection(); + const [mint, setMint] = useState(); + + const id = typeof key === 'string' ? key : key?.toBase58(); + + useEffect(() => { + if (!id) { + return; + } + + cache + .query(connection, id, MintParser) + .then(acc => setMint(acc.info as any)) + .catch(err => console.log(err)); + + const dispose = cache.emitter.onCache(e => { + const event = e; + if (event.id === id) { + cache + .query(connection, id, MintParser) + .then(mint => setMint(mint.info as any)); + } + }); + return () => { + dispose(); + }; + }, [connection, id]); + + return mint; +} + +export function useAccount(pubKey?: PublicKey) { + const connection = useConnection(); + const [account, setAccount] = useState(); + + const key = pubKey?.toBase58(); + useEffect(() => { + const query = async () => { + try { + if (!key) { + return; + } + + const acc = await cache + .query(connection, key, TokenAccountParser) + .catch(err => console.log(err)); + if (acc) { + setAccount(acc); + } + } catch (err) { + console.error(err); + } + }; + + query(); + + const dispose = cache.emitter.onCache(e => { + const event = e; + if (event.id === key) { + query(); + } + }); + return () => { + dispose(); + }; + }, [connection, key]); + + return account; +} diff --git a/js/packages/common/src/contexts/accounts/cache.ts b/js/packages/common/src/contexts/accounts/cache.ts new file mode 100644 index 0000000..5e88ffe --- /dev/null +++ b/js/packages/common/src/contexts/accounts/cache.ts @@ -0,0 +1,193 @@ +import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'; +import { MintInfo } from '@solana/spl-token'; +import { TokenAccount } from '../../models'; +import { EventEmitter } from '../../utils/eventEmitter'; +import { ParsedAccountBase, AccountParser } from './types'; +import { deserializeMint } from './deserialize'; + +export const genericCache = new Map(); +const mintCache = new Map(); +const pendingCalls = new Map>(); +const pendingMintCalls = new Map>(); + +const keyToAccountParser = new Map(); + +const getMintInfo = async (connection: Connection, pubKey: PublicKey) => { + const info = await connection.getAccountInfo(pubKey); + if (info === null) { + throw new Error('Failed to find mint account'); + } + + const data = Buffer.from(info.data); + + return deserializeMint(data); +}; + +export const cache = { + emitter: new EventEmitter(), + query: async ( + connection: Connection, + pubKey: string | PublicKey, + parser?: AccountParser, + ) => { + let id: PublicKey; + if (typeof pubKey === 'string') { + id = new PublicKey(pubKey); + } else { + id = pubKey; + } + + const address = id.toBase58(); + + const account = genericCache.get(address); + if (account) { + return account; + } + + let query = pendingCalls.get(address); + if (query) { + return query; + } + + // TODO: refactor to use multiple accounts query with flush like behavior + query = connection.getAccountInfo(id).then(data => { + if (!data) { + throw new Error('Account not found'); + } + + return cache.add(id, data, parser); + }) as Promise; + pendingCalls.set(address, query as any); + + return query; + }, + add: ( + id: PublicKey | string, + obj: AccountInfo, + parser?: AccountParser, + isActive?: boolean | undefined | ((parsed: any) => boolean), + ) => { + const address = typeof id === 'string' ? id : id?.toBase58(); + const deserialize = parser ? parser : keyToAccountParser.get(address); + if (!deserialize) { + throw new Error( + 'Deserializer needs to be registered or passed as a parameter', + ); + } + + cache.registerParser(id, deserialize); + pendingCalls.delete(address); + const account = deserialize(address, obj); + if (!account) { + return; + } + + if (isActive === undefined) isActive = true; + else if (isActive instanceof Function) isActive = isActive(account); + + const isNew = !genericCache.has(address); + + genericCache.set(address, account); + cache.emitter.raiseCacheUpdated(address, isNew, deserialize, isActive); + return account; + }, + get: (pubKey: string | PublicKey) => { + let key: string; + if (typeof pubKey !== 'string') { + key = pubKey.toBase58(); + } else { + key = pubKey; + } + + return genericCache.get(key); + }, + delete: (pubKey: string | PublicKey) => { + let key: string; + if (typeof pubKey !== 'string') { + key = pubKey.toBase58(); + } else { + key = pubKey; + } + + if (genericCache.get(key)) { + genericCache.delete(key); + cache.emitter.raiseCacheDeleted(key); + return true; + } + return false; + }, + + byParser: (parser: AccountParser) => { + const result: string[] = []; + for (const id of keyToAccountParser.keys()) { + if (keyToAccountParser.get(id) === parser) { + result.push(id); + } + } + + return result; + }, + registerParser: (pubkey: PublicKey | string, parser: AccountParser) => { + if (pubkey) { + const address = typeof pubkey === 'string' ? pubkey : pubkey?.toBase58(); + keyToAccountParser.set(address, parser); + } + + return pubkey; + }, + queryMint: async (connection: Connection, pubKey: string | PublicKey) => { + let id: PublicKey; + if (typeof pubKey === 'string') { + id = new PublicKey(pubKey); + } else { + id = pubKey; + } + + const address = id.toBase58(); + const mint = mintCache.get(address); + if (mint) { + return mint; + } + + let query = pendingMintCalls.get(address); + if (query) { + return query; + } + + query = getMintInfo(connection, id).then(data => { + pendingMintCalls.delete(address); + + mintCache.set(address, data); + return data; + }) as Promise; + pendingMintCalls.set(address, query as any); + + return query; + }, + getMint: (pubKey: string | PublicKey) => { + let key: string; + if (typeof pubKey !== 'string') { + key = pubKey.toBase58(); + } else { + key = pubKey; + } + + return mintCache.get(key); + }, + addMint: (pubKey: PublicKey, obj: AccountInfo) => { + const mint = deserializeMint(obj.data); + const id = pubKey.toBase58(); + mintCache.set(id, mint); + return mint; + }, +}; + +export const getCachedAccount = ( + predicate: (account: TokenAccount) => boolean, +) => { + for (const account of genericCache.values()) { + if (predicate(account)) { + return account as TokenAccount; + } + } +}; diff --git a/js/packages/common/src/contexts/accounts/deserialize.ts b/js/packages/common/src/contexts/accounts/deserialize.ts new file mode 100644 index 0000000..a5ef201 --- /dev/null +++ b/js/packages/common/src/contexts/accounts/deserialize.ts @@ -0,0 +1,63 @@ +import { PublicKey } from '@solana/web3.js'; +import { AccountLayout, MintInfo, MintLayout, u64 } from '@solana/spl-token'; + +// TODO: expose in spl package +export const deserializeAccount = (data: Buffer) => { + const accountInfo = AccountLayout.decode(data); + accountInfo.mint = new PublicKey(accountInfo.mint); + accountInfo.owner = new PublicKey(accountInfo.owner); + accountInfo.amount = u64.fromBuffer(accountInfo.amount); + + if (accountInfo.delegateOption === 0) { + accountInfo.delegate = null; + accountInfo.delegatedAmount = new u64(0); + } else { + accountInfo.delegate = new PublicKey(accountInfo.delegate); + accountInfo.delegatedAmount = u64.fromBuffer(accountInfo.delegatedAmount); + } + + accountInfo.isInitialized = accountInfo.state !== 0; + accountInfo.isFrozen = accountInfo.state === 2; + + if (accountInfo.isNativeOption === 1) { + accountInfo.rentExemptReserve = u64.fromBuffer(accountInfo.isNative); + accountInfo.isNative = true; + } else { + accountInfo.rentExemptReserve = null; + accountInfo.isNative = false; + } + + if (accountInfo.closeAuthorityOption === 0) { + accountInfo.closeAuthority = null; + } else { + accountInfo.closeAuthority = new PublicKey(accountInfo.closeAuthority); + } + + return accountInfo; +}; + +// TODO: expose in spl package +export const deserializeMint = (data: Buffer) => { + if (data.length !== MintLayout.span) { + throw new Error('Not a valid Mint'); + } + + const mintInfo = MintLayout.decode(data); + + if (mintInfo.mintAuthorityOption === 0) { + mintInfo.mintAuthority = null; + } else { + mintInfo.mintAuthority = new PublicKey(mintInfo.mintAuthority); + } + + mintInfo.supply = u64.fromBuffer(mintInfo.supply); + mintInfo.isInitialized = mintInfo.isInitialized !== 0; + + if (mintInfo.freezeAuthorityOption === 0) { + mintInfo.freezeAuthority = null; + } else { + mintInfo.freezeAuthority = new PublicKey(mintInfo.freezeAuthority); + } + + return mintInfo as MintInfo; +}; diff --git a/js/packages/common/src/contexts/accounts/getMultipleAccounts.ts b/js/packages/common/src/contexts/accounts/getMultipleAccounts.ts new file mode 100644 index 0000000..d0611c4 --- /dev/null +++ b/js/packages/common/src/contexts/accounts/getMultipleAccounts.ts @@ -0,0 +1,56 @@ +import { AccountInfo } from '@solana/web3.js'; +import { chunks } from '../../utils/utils'; + +export const getMultipleAccounts = async ( + connection: any, + keys: string[], + commitment: string, +) => { + const result = await Promise.all( + chunks(keys, 99).map(chunk => + getMultipleAccountsCore(connection, chunk, commitment), + ), + ); + + const array = result + .map( + a => + a.array.map(acc => { + if (!acc) { + return undefined; + } + + const { data, ...rest } = acc; + const obj = { + ...rest, + data: Buffer.from(data[0], 'base64'), + } as AccountInfo; + return obj; + }) as AccountInfo[], + ) + .flat(); + return { keys, array }; +}; + +const getMultipleAccountsCore = async ( + connection: any, + keys: string[], + commitment: string, +) => { + const args = connection._buildArgs([keys], commitment, 'base64'); + + const unsafeRes = await connection._rpcRequest('getMultipleAccounts', args); + if (unsafeRes.error) { + throw new Error( + 'failed to get info about account ' + unsafeRes.error.message, + ); + } + + if (unsafeRes.result.value) { + const array = unsafeRes.result.value as AccountInfo[]; + return { keys, array }; + } + + // TODO: fix + throw new Error(); +}; diff --git a/js/packages/common/src/contexts/accounts/index.ts b/js/packages/common/src/contexts/accounts/index.ts new file mode 100644 index 0000000..8c602f0 --- /dev/null +++ b/js/packages/common/src/contexts/accounts/index.ts @@ -0,0 +1,6 @@ +export * from './accounts'; +export * from './cache'; +export * from './getMultipleAccounts'; +export * from './parsesrs'; +export * from './deserialize'; +export * from './types'; diff --git a/js/packages/common/src/contexts/accounts/parsesrs.ts b/js/packages/common/src/contexts/accounts/parsesrs.ts new file mode 100644 index 0000000..174ab9b --- /dev/null +++ b/js/packages/common/src/contexts/accounts/parsesrs.ts @@ -0,0 +1,64 @@ +import { AccountInfo } from '@solana/web3.js'; +import { TokenAccount } from '../../models'; +import { ParsedAccountBase } from './types'; +import { deserializeMint, deserializeAccount } from './deserialize'; +import { StringPublicKey } from '../../utils'; + +export const MintParser = ( + pubKey: StringPublicKey, + info: AccountInfo, +) => { + const buffer = Buffer.from(info.data); + + const data = deserializeMint(buffer); + + const details = { + pubkey: pubKey, + account: { + ...info, + }, + info: data, + } as ParsedAccountBase; + + return details; +}; + +export const TokenAccountParser = ( + pubKey: StringPublicKey, + info: AccountInfo, +) => { + // Sometimes a wrapped sol account gets closed, goes to 0 length, + // triggers an update over wss which triggers this guy to get called + // since your UI already logged that pubkey as a token account. Check for length. + if (info.data.length > 0) { + const buffer = Buffer.from(info.data); + const data = deserializeAccount(buffer); + + const details = { + pubkey: pubKey, + account: { + ...info, + }, + info: data, + } as TokenAccount; + + return details; + } +}; + +export const GenericAccountParser = ( + pubKey: StringPublicKey, + info: AccountInfo, +) => { + const buffer = Buffer.from(info.data); + + const details = { + pubkey: pubKey, + account: { + ...info, + }, + info: buffer, + } as ParsedAccountBase; + + return details; +}; diff --git a/js/packages/common/src/contexts/accounts/types.ts b/js/packages/common/src/contexts/accounts/types.ts new file mode 100644 index 0000000..a44a3ca --- /dev/null +++ b/js/packages/common/src/contexts/accounts/types.ts @@ -0,0 +1,17 @@ +import { AccountInfo } from '@solana/web3.js'; +import { StringPublicKey } from '../../utils'; + +export interface ParsedAccountBase { + pubkey: StringPublicKey; + account: AccountInfo; + info: any; // TODO: change to unknown +} + +export type AccountParser = ( + pubkey: StringPublicKey, + data: AccountInfo, +) => ParsedAccountBase | undefined; + +export interface ParsedAccount extends ParsedAccountBase { + info: T; +} diff --git a/js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts b/js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts index 310a59a..b296e8a 100644 --- a/js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts +++ b/js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts @@ -1,6 +1,6 @@ import { Metadata } from '../../actions'; import { Store, WhitelistedCreator } from '../../models/metaplex'; -import { ParsedAccount } from '../accounts'; +import { ParsedAccount } from '../accounts/types'; export const isMetadataPartOfStore = ( m: ParsedAccount, diff --git a/js/packages/common/src/contexts/meta/loadAccounts.ts b/js/packages/common/src/contexts/meta/loadAccounts.ts index 0d37c84..d0ca5dc 100644 --- a/js/packages/common/src/contexts/meta/loadAccounts.ts +++ b/js/packages/common/src/contexts/meta/loadAccounts.ts @@ -24,7 +24,8 @@ import { processAuctions } from './processAuctions'; import { processMetaplexAccounts } from './processMetaplexAccounts'; import { processMetaData } from './processMetaData'; import { processVaultData } from './processVaultData'; -import { ParsedAccount, getMultipleAccounts } from '../accounts'; +import { ParsedAccount } from '../accounts/types'; +import { getMultipleAccounts } from '../accounts'; async function getProgramAccounts( connection: Connection, diff --git a/js/packages/common/src/contexts/meta/processAuctions.ts b/js/packages/common/src/contexts/meta/processAuctions.ts index 701eb82..b1f4105 100644 --- a/js/packages/common/src/contexts/meta/processAuctions.ts +++ b/js/packages/common/src/contexts/meta/processAuctions.ts @@ -12,7 +12,8 @@ import { MAX_AUCTION_DATA_EXTENDED_SIZE, } from '../../actions'; import { AUCTION_ID } from '../../utils'; -import { cache, ParsedAccount } from '../accounts'; +import { ParsedAccount } from '../accounts/types'; +import { cache } from '../accounts/cache'; import { CheckAccountFunc, ProcessAccountsFunc } from './types'; export const processAuctions: ProcessAccountsFunc = ( diff --git a/js/packages/common/src/contexts/meta/processMetaData.ts b/js/packages/common/src/contexts/meta/processMetaData.ts index 7570000..03020bc 100644 --- a/js/packages/common/src/contexts/meta/processMetaData.ts +++ b/js/packages/common/src/contexts/meta/processMetaData.ts @@ -11,7 +11,7 @@ import { Metadata, MetadataKey, } from '../../actions'; -import { ParsedAccount } from '../accounts'; +import { ParsedAccount } from '../accounts/types'; import { METADATA_PROGRAM_ID } from '../../utils'; export const processMetaData: ProcessAccountsFunc = ( diff --git a/js/packages/common/src/contexts/meta/processMetaplexAccounts.ts b/js/packages/common/src/contexts/meta/processMetaplexAccounts.ts index 3eabb70..ba62317 100644 --- a/js/packages/common/src/contexts/meta/processMetaplexAccounts.ts +++ b/js/packages/common/src/contexts/meta/processMetaplexAccounts.ts @@ -22,7 +22,8 @@ import { } from '../../models/metaplex'; import { ProcessAccountsFunc } from './types'; import { METAPLEX_ID, programIds } from '../../utils'; -import { cache, ParsedAccount } from '../accounts'; +import { ParsedAccount } from '../accounts/types'; +import { cache } from '../accounts/cache'; export const processMetaplexAccounts: ProcessAccountsFunc = async ( { account, pubkey }, diff --git a/js/packages/common/src/contexts/meta/processVaultData.ts b/js/packages/common/src/contexts/meta/processVaultData.ts index af51412..1ab148b 100644 --- a/js/packages/common/src/contexts/meta/processVaultData.ts +++ b/js/packages/common/src/contexts/meta/processVaultData.ts @@ -7,7 +7,7 @@ import { VaultKey, } from '../../actions'; import { VAULT_ID } from '../../utils'; -import { ParsedAccount } from '../accounts'; +import { ParsedAccount } from '../accounts/types'; import { ProcessAccountsFunc } from './types'; export const processVaultData: ProcessAccountsFunc = ( diff --git a/js/packages/common/src/contexts/meta/queryExtendedMetadata.ts b/js/packages/common/src/contexts/meta/queryExtendedMetadata.ts index 1f763b6..f265168 100644 --- a/js/packages/common/src/contexts/meta/queryExtendedMetadata.ts +++ b/js/packages/common/src/contexts/meta/queryExtendedMetadata.ts @@ -1,12 +1,10 @@ import { MintInfo } from '@solana/spl-token'; import { Connection } from '@solana/web3.js'; import { Metadata } from '../../actions'; -import { - cache, - getMultipleAccounts, - MintParser, - ParsedAccount, -} from '../accounts'; +import { ParsedAccount } from '../accounts/types'; +import { cache } from '../accounts/cache'; +import { getMultipleAccounts } from '../accounts/getMultipleAccounts'; +import { MintParser } from '../accounts/parsesrs'; export const queryExtendedMetadata = async ( connection: Connection, diff --git a/js/packages/common/src/contexts/meta/types.ts b/js/packages/common/src/contexts/meta/types.ts index b4d3109..950af1e 100644 --- a/js/packages/common/src/contexts/meta/types.ts +++ b/js/packages/common/src/contexts/meta/types.ts @@ -22,8 +22,8 @@ import { Store, WhitelistedCreator, } from '../../models/metaplex'; -import { ParsedAccount } from '../accounts'; import { PublicKeyStringAndAccount } from '../../utils'; +import { ParsedAccount } from '../accounts/types'; export interface MetaState { metadata: ParsedAccount[]; diff --git a/js/packages/common/src/utils/ids.ts b/js/packages/common/src/utils/ids.ts index 6816fff..6f53b00 100644 --- a/js/packages/common/src/utils/ids.ts +++ b/js/packages/common/src/utils/ids.ts @@ -36,6 +36,10 @@ export const toPublicKey = (key: string | PublicKey) => { return result; }; +export const pubkeyToString = (key: PublicKey | null | string = '') => { + return typeof key === 'string' ? key : key?.toBase58() || ''; +}; + export interface PublicKeyStringAndAccount { pubkey: string; account: AccountInfo; diff --git a/js/packages/web/src/contexts/index.tsx b/js/packages/web/src/contexts/index.tsx index ea3d305..0241456 100644 --- a/js/packages/web/src/contexts/index.tsx +++ b/js/packages/web/src/contexts/index.tsx @@ -1,2 +1,2 @@ -export * from '@oyster/common/dist/lib/contexts/meta/meta' +export * from '@oyster/common/dist/lib/contexts/meta/meta'; export * from './coingecko'; diff --git a/js/packages/web/src/views/admin/index.tsx b/js/packages/web/src/views/admin/index.tsx index bac0be1..f619d5f 100644 --- a/js/packages/web/src/views/admin/index.tsx +++ b/js/packages/web/src/views/admin/index.tsx @@ -12,7 +12,10 @@ import { Divider, } from 'antd'; import { useMeta } from '../../contexts'; -import { Store, WhitelistedCreator } from '@oyster/common/dist/lib/models/metaplex/index'; +import { + Store, + WhitelistedCreator, +} from '@oyster/common/dist/lib/models/metaplex/index'; import { MasterEditionV1, notify, diff --git a/js/packages/web/src/views/analytics/index.tsx b/js/packages/web/src/views/analytics/index.tsx index c7f008e..90429c5 100644 --- a/js/packages/web/src/views/analytics/index.tsx +++ b/js/packages/web/src/views/analytics/index.tsx @@ -1,7 +1,11 @@ import React, { Dispatch, SetStateAction, useState } from 'react'; import { Layout, Button, Col, Spin } from 'antd'; import { useMeta } from '../../contexts'; -import { AuctionManagerV1, AuctionManagerV2, WinningConfigType } from '@oyster/common/dist/lib/models/metaplex/index'; +import { + AuctionManagerV1, + AuctionManagerV2, + WinningConfigType, +} from '@oyster/common/dist/lib/models/metaplex/index'; import { Pie, Bar } from 'react-chartjs-2'; import { AuctionDataExtended, From 5dadcd9ee7c19f97ab480914c3c356d63dd2509d Mon Sep 17 00:00:00 2001 From: exromany Date: Wed, 4 Aug 2021 19:32:55 +0300 Subject: [PATCH 04/10] useLocalStorage hook --- js/packages/common/src/utils/index.tsx | 1 + .../common/src/utils/useLocalStorage.ts | 32 +++++++++++++++++++ js/packages/common/src/utils/utils.ts | 3 ++ js/packages/web/src/hooks/useArt.ts | 2 ++ js/packages/web/src/utils/assets.ts | 2 ++ 5 files changed, 40 insertions(+) create mode 100644 js/packages/common/src/utils/useLocalStorage.ts diff --git a/js/packages/common/src/utils/index.tsx b/js/packages/common/src/utils/index.tsx index ea95b4e..e27e980 100644 --- a/js/packages/common/src/utils/index.tsx +++ b/js/packages/common/src/utils/index.tsx @@ -4,6 +4,7 @@ export * from './programIds'; export * as Layout from './layout'; export * from './notifications'; export * from './utils'; +export * from './useLocalStorage'; export * from './strings'; export * as shortvec from './shortvec'; export * from './isValidHttpUrl'; diff --git a/js/packages/common/src/utils/useLocalStorage.ts b/js/packages/common/src/utils/useLocalStorage.ts new file mode 100644 index 0000000..e030fce --- /dev/null +++ b/js/packages/common/src/utils/useLocalStorage.ts @@ -0,0 +1,32 @@ +type UseStorageReturnValue = { + getItem: (key: string) => string; + setItem: (key: string, value: string) => boolean; + removeItem: (key: string) => void; +}; + +export const useLocalStorage = (): UseStorageReturnValue => { + const isBrowser: boolean = ((): boolean => typeof window !== 'undefined')(); + + const getItem = (key: string): string => { + return isBrowser ? window.localStorage[key] : ''; + }; + + const setItem = (key: string, value: string): boolean => { + if (isBrowser) { + window.localStorage.setItem(key, value); + return true; + } + + return false; + }; + + const removeItem = (key: string): void => { + window.localStorage.removeItem(key); + }; + + return { + getItem, + setItem, + removeItem, + }; +}; diff --git a/js/packages/common/src/utils/utils.ts b/js/packages/common/src/utils/utils.ts index 9fa7b30..e03c887 100644 --- a/js/packages/common/src/utils/utils.ts +++ b/js/packages/common/src/utils/utils.ts @@ -6,6 +6,7 @@ import { PublicKey } from '@solana/web3.js'; import BN from 'bn.js'; import { WAD, ZERO } from '../constants'; import { TokenInfo } from '@solana/spl-token-registry'; +import { useLocalStorage } from './useLocalStorage'; export type KnownTokenMap = Map; @@ -16,6 +17,7 @@ export const formatPriceNumber = new Intl.NumberFormat('en-US', { }); export function useLocalStorageState(key: string, defaultState?: string) { + const localStorage = useLocalStorage(); const [state, setState] = useState(() => { // NOTE: Not sure if this is ok const storedState = localStorage.getItem(key); @@ -52,6 +54,7 @@ export const findProgramAddress = async ( seeds: (Buffer | Uint8Array)[], programId: PublicKey, ) => { + const localStorage = useLocalStorage(); const key = 'pda-' + seeds.reduce((agg, item) => agg + item.toString('hex'), '') + diff --git a/js/packages/web/src/hooks/useArt.ts b/js/packages/web/src/hooks/useArt.ts index 6ab9c9d..a7c948e 100644 --- a/js/packages/web/src/hooks/useArt.ts +++ b/js/packages/web/src/hooks/useArt.ts @@ -9,6 +9,7 @@ import { Metadata, ParsedAccount, StringPublicKey, + useLocalStorage, } from '@oyster/common'; import { WhitelistedCreator } from '@oyster/common/dist/lib/models/metaplex/index'; import { Cache } from 'three'; @@ -159,6 +160,7 @@ export const useExtendedArt = (id?: StringPublicKey) => { const [data, setData] = useState(); const { ref, inView } = useInView(); + const localStorage = useLocalStorage(); const key = pubkeyToString(id); diff --git a/js/packages/web/src/utils/assets.ts b/js/packages/web/src/utils/assets.ts index 6d191f0..5e20f5d 100644 --- a/js/packages/web/src/utils/assets.ts +++ b/js/packages/web/src/utils/assets.ts @@ -1,3 +1,4 @@ +import { useLocalStorage } from '@oyster/common'; import { TokenInfo } from '@solana/spl-token-registry'; export const LAMPORT_MULTIPLIER = 10 ** 9; @@ -8,6 +9,7 @@ export const filterModalSolTokens = (tokens: TokenInfo[]) => { }; export async function getAssetCostToStore(files: File[]) { + const localStorage = useLocalStorage(); const totalBytes = files.reduce((sum, f) => (sum += f.size), 0); console.log('Total bytes', totalBytes); const txnFeeInWinstons = parseInt( From 921abd38905dd51b32628179cec736e72bf1a967 Mon Sep 17 00:00:00 2001 From: exromany Date: Mon, 16 Aug 2021 12:43:39 +0300 Subject: [PATCH 05/10] [meta] keep all creators --- .../src/contexts/meta/getEmptyMetaState.ts | 27 ++++ js/packages/common/src/contexts/meta/index.ts | 1 + .../contexts/meta/isMetadataPartOfStore.ts | 4 - .../common/src/contexts/meta/loadAccounts.ts | 27 +--- js/packages/common/src/contexts/meta/meta.tsx | 137 ++---------------- .../src/contexts/meta/onChangeAccount.ts | 14 +- .../contexts/meta/processMetaplexAccounts.ts | 38 +++-- .../contexts/meta/subscribeAccountsChange.ts | 76 ++++++++++ js/packages/common/src/contexts/meta/types.ts | 1 + .../common/src/models/metaplex/index.ts | 17 ++- .../web/src/components/ArtContent/index.tsx | 3 +- js/packages/web/src/hooks/useArt.ts | 2 +- js/packages/web/src/hooks/useCreator.ts | 3 +- js/packages/web/src/utils/extendCreator.ts | 8 + js/packages/web/src/utils/pubkeyToString.ts | 5 - 15 files changed, 169 insertions(+), 194 deletions(-) create mode 100644 js/packages/common/src/contexts/meta/getEmptyMetaState.ts create mode 100644 js/packages/common/src/contexts/meta/subscribeAccountsChange.ts create mode 100644 js/packages/web/src/utils/extendCreator.ts delete mode 100644 js/packages/web/src/utils/pubkeyToString.ts diff --git a/js/packages/common/src/contexts/meta/getEmptyMetaState.ts b/js/packages/common/src/contexts/meta/getEmptyMetaState.ts new file mode 100644 index 0000000..bd6a1db --- /dev/null +++ b/js/packages/common/src/contexts/meta/getEmptyMetaState.ts @@ -0,0 +1,27 @@ +import { MetaState } from './types'; + +export const getEmptyMetaState = (): MetaState => ({ + metadata: [], + metadataByMint: {}, + masterEditions: {}, + masterEditionsByPrintingMint: {}, + masterEditionsByOneTimeAuthMint: {}, + metadataByMasterEdition: {}, + editions: {}, + auctionManagersByAuction: {}, + bidRedemptions: {}, + auctions: {}, + auctionDataExtended: {}, + vaults: {}, + payoutTickets: {}, + store: null, + whitelistedCreatorsByCreator: {}, + bidderMetadataByAuctionAndBidder: {}, + bidderPotsByAuctionAndBidder: {}, + safetyDepositBoxesByVaultAndIndex: {}, + prizeTrackingTickets: {}, + safetyDepositConfigsByAuctionManagerAndIndex: {}, + bidRedemptionV2sByAuctionManagerAndWinningIndex: {}, + stores: {}, + creators: {}, +}); diff --git a/js/packages/common/src/contexts/meta/index.ts b/js/packages/common/src/contexts/meta/index.ts index 1cc9367..cdf9404 100644 --- a/js/packages/common/src/contexts/meta/index.ts +++ b/js/packages/common/src/contexts/meta/index.ts @@ -2,6 +2,7 @@ export * from './meta'; export * from './isMetadataPartOfStore'; export * from './loadAccounts'; export * from './onChangeAccount'; +export * from './subscribeAccountsChange'; export * from './processAuctions'; export * from './processMetaData'; export * from './processMetaplexAccounts'; diff --git a/js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts b/js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts index b296e8a..2bcf158 100644 --- a/js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts +++ b/js/packages/common/src/contexts/meta/isMetadataPartOfStore.ts @@ -9,11 +9,7 @@ export const isMetadataPartOfStore = ( string, ParsedAccount >, - useAll: boolean, ) => { - if (useAll) { - return true; - } if (!m?.info?.data?.creators || !store?.info) { return false; } diff --git a/js/packages/common/src/contexts/meta/loadAccounts.ts b/js/packages/common/src/contexts/meta/loadAccounts.ts index d0ca5dc..3878196 100644 --- a/js/packages/common/src/contexts/meta/loadAccounts.ts +++ b/js/packages/common/src/contexts/meta/loadAccounts.ts @@ -26,6 +26,7 @@ import { processMetaData } from './processMetaData'; import { processVaultData } from './processVaultData'; import { ParsedAccount } from '../accounts/types'; import { getMultipleAccounts } from '../accounts'; +import { getEmptyMetaState } from './getEmptyMetaState'; async function getProgramAccounts( connection: Connection, @@ -82,30 +83,7 @@ async function getProgramAccounts( } export const loadAccounts = async (connection: Connection, all: boolean) => { - const tempCache: MetaState = { - metadata: [], - metadataByMint: {}, - masterEditions: {}, - masterEditionsByPrintingMint: {}, - masterEditionsByOneTimeAuthMint: {}, - metadataByMasterEdition: {}, - editions: {}, - auctionManagersByAuction: {}, - bidRedemptions: {}, - auctions: {}, - auctionDataExtended: {}, - vaults: {}, - payoutTickets: {}, - store: null, - whitelistedCreatorsByCreator: {}, - bidderMetadataByAuctionAndBidder: {}, - bidderPotsByAuctionAndBidder: {}, - safetyDepositBoxesByVaultAndIndex: {}, - prizeTrackingTickets: {}, - safetyDepositConfigsByAuctionManagerAndIndex: {}, - bidRedemptionV2sByAuctionManagerAndWinningIndex: {}, - stores: {}, - }; + const tempCache: MetaState = getEmptyMetaState(); const updateTemp = makeSetter(tempCache); const forEach = @@ -298,7 +276,6 @@ export const metadataByMintUpdater = async ( metadata, state.store, state.whitelistedCreatorsByCreator, - all, ) ) { await metadata.info.init(); diff --git a/js/packages/common/src/contexts/meta/meta.tsx b/js/packages/common/src/contexts/meta/meta.tsx index c243f7e..facd1da 100644 --- a/js/packages/common/src/contexts/meta/meta.tsx +++ b/js/packages/common/src/contexts/meta/meta.tsx @@ -1,57 +1,16 @@ -import React, { - useCallback, - useContext, - useEffect, - useMemo, - useState, -} from 'react'; -import { MetaState, MetaContextState, UpdateStateValueFunc } from './types'; +import React, { useCallback, useContext, useEffect, useState } from 'react'; import { queryExtendedMetadata } from './queryExtendedMetadata'; -import { processAuctions } from './processAuctions'; -import { processMetaplexAccounts } from './processMetaplexAccounts'; -import { processMetaData } from './processMetaData'; -import { processVaultData } from './processVaultData'; -import { - loadAccounts, - makeSetter, - metadataByMintUpdater, -} from './loadAccounts'; -import { onChangeAccount } from './onChangeAccount'; +import { subscribeAccountsChange } from './subscribeAccountsChange'; +import { getEmptyMetaState } from './getEmptyMetaState'; +import { loadAccounts } from './loadAccounts'; +import { MetaContextState, MetaState } from './types'; import { useConnection } from '../connection'; -import { - AUCTION_ID, - METADATA_PROGRAM_ID, - METAPLEX_ID, - toPublicKey, - VAULT_ID, -} from '../../utils'; -import { useQuerySearch } from '../../hooks'; import { useStore } from '../store'; +import { useQuerySearch } from '../../hooks'; const MetaContext = React.createContext({ - metadata: [], - metadataByMint: {}, - masterEditions: {}, - masterEditionsByPrintingMint: {}, - masterEditionsByOneTimeAuthMint: {}, - metadataByMasterEdition: {}, - editions: {}, - auctionManagersByAuction: {}, - auctions: {}, - auctionDataExtended: {}, - vaults: {}, - store: null, + ...getEmptyMetaState(), isLoading: false, - bidderMetadataByAuctionAndBidder: {}, - safetyDepositBoxesByVaultAndIndex: {}, - safetyDepositConfigsByAuctionManagerAndIndex: {}, - bidRedemptionV2sByAuctionManagerAndWinningIndex: {}, - bidderPotsByAuctionAndBidder: {}, - bidRedemptions: {}, - whitelistedCreatorsByCreator: {}, - payoutTickets: {}, - prizeTrackingTickets: {}, - stores: {}, }); export function MetaProvider({ children = null as any }) { @@ -60,30 +19,7 @@ export function MetaProvider({ children = null as any }) { const searchParams = useQuerySearch(); const all = searchParams.get('all') == 'true'; - const [state, setState] = useState({ - metadata: [], - metadataByMint: {}, - masterEditions: {}, - masterEditionsByPrintingMint: {}, - masterEditionsByOneTimeAuthMint: {}, - metadataByMasterEdition: {}, - editions: {}, - auctionManagersByAuction: {}, - bidRedemptions: {}, - auctions: {}, - auctionDataExtended: {}, - vaults: {}, - payoutTickets: {}, - store: null, - whitelistedCreatorsByCreator: {}, - bidderMetadataByAuctionAndBidder: {}, - bidderPotsByAuctionAndBidder: {}, - safetyDepositBoxesByVaultAndIndex: {}, - prizeTrackingTickets: {}, - safetyDepositConfigsByAuctionManagerAndIndex: {}, - bidRedemptionV2sByAuctionManagerAndWinningIndex: {}, - stores: {}, - }); + const [state, setState] = useState(getEmptyMetaState()); const [isLoading, setIsLoading] = useState(true); @@ -134,66 +70,13 @@ export function MetaProvider({ children = null as any }) { })(); }, [connection, setState, updateMints, storeAddress, isReady]); - const updateStateValue = useMemo( - () => (prop, key, value) => { - setState(current => makeSetter({ ...current })(prop, key, value)); - }, - [setState], - ); - - const store = state.store; - const whitelistedCreatorsByCreator = state.whitelistedCreatorsByCreator; - useEffect(() => { if (isLoading) { return; } - const vaultSubId = connection.onProgramAccountChange( - toPublicKey(VAULT_ID), - onChangeAccount(processVaultData, updateStateValue, all), - ); - - const auctionSubId = connection.onProgramAccountChange( - toPublicKey(AUCTION_ID), - onChangeAccount(processAuctions, updateStateValue, all), - ); - - const metaplexSubId = connection.onProgramAccountChange( - toPublicKey(METAPLEX_ID), - onChangeAccount(processMetaplexAccounts, updateStateValue, all), - ); - - const metaSubId = connection.onProgramAccountChange( - toPublicKey(METADATA_PROGRAM_ID), - onChangeAccount( - processMetaData, - async (prop, key, value) => { - if (prop === 'metadataByMint') { - const nextState = await metadataByMintUpdater(value, state, all); - setState(nextState); - } else { - updateStateValue(prop, key, value); - } - }, - all, - ), - ); - - return () => { - connection.removeProgramAccountChangeListener(vaultSubId); - connection.removeProgramAccountChangeListener(metaplexSubId); - connection.removeProgramAccountChangeListener(metaSubId); - connection.removeProgramAccountChangeListener(auctionSubId); - }; - }, [ - connection, - updateStateValue, - setState, - store, - whitelistedCreatorsByCreator, - isLoading, - ]); + return subscribeAccountsChange(connection, all, () => state, setState); + }, [connection, setState, isLoading]); // TODO: fetch names dynamically // TODO: get names for creators diff --git a/js/packages/common/src/contexts/meta/onChangeAccount.ts b/js/packages/common/src/contexts/meta/onChangeAccount.ts index c9a01bc..273204a 100644 --- a/js/packages/common/src/contexts/meta/onChangeAccount.ts +++ b/js/packages/common/src/contexts/meta/onChangeAccount.ts @@ -1,7 +1,5 @@ -import { - KeyedAccountInfo, - ProgramAccountChangeCallback, -} from '@solana/web3.js'; +import { ProgramAccountChangeCallback } from '@solana/web3.js'; +import { pubkeyToString } from '../../utils'; import { ProcessAccountsFunc, UpdateStateValueFunc } from './types'; export const onChangeAccount = @@ -11,7 +9,7 @@ export const onChangeAccount = all: boolean, ): ProgramAccountChangeCallback => async info => { - const pubkey = pubkeyByAccountInfo(info); + const pubkey = pubkeyToString(info.accountId); await process( { pubkey, @@ -21,9 +19,3 @@ export const onChangeAccount = all, ); }; - -const pubkeyByAccountInfo = (info: KeyedAccountInfo) => { - return typeof info.accountId === 'string' - ? info.accountId - : info.accountId.toBase58(); -}; diff --git a/js/packages/common/src/contexts/meta/processMetaplexAccounts.ts b/js/packages/common/src/contexts/meta/processMetaplexAccounts.ts index ba62317..da324f1 100644 --- a/js/packages/common/src/contexts/meta/processMetaplexAccounts.ts +++ b/js/packages/common/src/contexts/meta/processMetaplexAccounts.ts @@ -6,8 +6,7 @@ import { decodeAuctionManager, decodeBidRedemptionTicket, decodeStore, - decodeWhitelistedCreator, - getWhitelistedCreator, + isCreatorPartOfTheStore, MetaplexKey, Store, WhitelistedCreator, @@ -131,24 +130,33 @@ export const processMetaplexAccounts: ProcessAccountsFunc = async ( } if (isWhitelistedCreatorV1Account(account)) { - const whitelistedCreator = decodeWhitelistedCreator(account.data); + const parsedAccount = cache.add( + pubkey, + account, + WhitelistedCreatorParser, + false, + ) as ParsedAccount; + // TODO: figure out a way to avoid generating creator addresses during parsing // should we store store id inside creator? - const creatorKeyIfCreatorWasPartOfThisStore = await getWhitelistedCreator( - whitelistedCreator.address, - ); - - if (creatorKeyIfCreatorWasPartOfThisStore === pubkey) { - const parsedAccount = cache.add( + if (STORE_ID) { + const isWhitelistedCreator = await isCreatorPartOfTheStore( + parsedAccount.info.address, pubkey, - account, - WhitelistedCreatorParser, - false, - ) as ParsedAccount; + ); + if (isWhitelistedCreator) { + setter( + 'whitelistedCreatorsByCreator', + parsedAccount.info.address, + parsedAccount, + ); + } + } + if (useAll) { setter( - 'whitelistedCreatorsByCreator', - whitelistedCreator.address, + 'creators', + parsedAccount.info.address + '-' + pubkey, parsedAccount, ); } diff --git a/js/packages/common/src/contexts/meta/subscribeAccountsChange.ts b/js/packages/common/src/contexts/meta/subscribeAccountsChange.ts new file mode 100644 index 0000000..d1c32cb --- /dev/null +++ b/js/packages/common/src/contexts/meta/subscribeAccountsChange.ts @@ -0,0 +1,76 @@ +import { Connection } from '@solana/web3.js'; +import { + AUCTION_ID, + METADATA_PROGRAM_ID, + METAPLEX_ID, + toPublicKey, + VAULT_ID, +} from '../../utils'; +import { makeSetter, metadataByMintUpdater } from './loadAccounts'; +import { onChangeAccount } from './onChangeAccount'; +import { processAuctions } from './processAuctions'; +import { processMetaData } from './processMetaData'; +import { processMetaplexAccounts } from './processMetaplexAccounts'; +import { processVaultData } from './processVaultData'; +import { MetaState, UpdateStateValueFunc } from './types'; + +export const subscribeAccountsChange = ( + connection: Connection, + all: boolean, + getState: () => MetaState, + setState: (v: MetaState) => void, +) => { + const subscriptions: number[] = []; + + const updateStateValue: UpdateStateValueFunc = (prop, key, value) => { + const state = getState(); + const nextState = makeSetter({ ...state })(prop, key, value); + setState(nextState); + }; + + subscriptions.push( + connection.onProgramAccountChange( + toPublicKey(VAULT_ID), + onChangeAccount(processVaultData, updateStateValue, all), + ), + ); + + subscriptions.push( + connection.onProgramAccountChange( + toPublicKey(AUCTION_ID), + onChangeAccount(processAuctions, updateStateValue, all), + ), + ); + + subscriptions.push( + connection.onProgramAccountChange( + toPublicKey(METAPLEX_ID), + onChangeAccount(processMetaplexAccounts, updateStateValue, all), + ), + ); + + subscriptions.push( + connection.onProgramAccountChange( + toPublicKey(METADATA_PROGRAM_ID), + onChangeAccount( + processMetaData, + async (prop, key, value) => { + if (prop === 'metadataByMint') { + const state = getState(); + const nextState = await metadataByMintUpdater(value, state, all); + setState(nextState); + } else { + updateStateValue(prop, key, value); + } + }, + all, + ), + ), + ); + + return () => { + subscriptions.forEach(subscriptionId => { + connection.removeProgramAccountChangeListener(subscriptionId); + }); + }; +}; diff --git a/js/packages/common/src/contexts/meta/types.ts b/js/packages/common/src/contexts/meta/types.ts index 950af1e..e41f7f0 100644 --- a/js/packages/common/src/contexts/meta/types.ts +++ b/js/packages/common/src/contexts/meta/types.ts @@ -72,6 +72,7 @@ export interface MetaState { >; payoutTickets: Record>; stores: Record>; + creators: Record>; } export interface MetaContextState extends MetaState { diff --git a/js/packages/common/src/models/metaplex/index.ts b/js/packages/common/src/models/metaplex/index.ts index 47524e9..237afb5 100644 --- a/js/packages/common/src/models/metaplex/index.ts +++ b/js/packages/common/src/models/metaplex/index.ts @@ -1096,9 +1096,22 @@ export async function getOriginalAuthority( )[0]; } -export async function getWhitelistedCreator(creator: string) { +export const isCreatorPartOfTheStore = async ( + creatorAddress: StringPublicKey, + pubkey: StringPublicKey, + store?: StringPublicKey, +) => { + const creatorKeyInStore = await getWhitelistedCreator(creatorAddress, store); + + return creatorKeyInStore === pubkey; +}; + +export async function getWhitelistedCreator( + creator: StringPublicKey, + storeId?: StringPublicKey, +) { const PROGRAM_IDS = programIds(); - const store = PROGRAM_IDS.store; + const store = storeId || PROGRAM_IDS.store; if (!store) { throw new Error('Store not initialized'); } diff --git a/js/packages/web/src/components/ArtContent/index.tsx b/js/packages/web/src/components/ArtContent/index.tsx index bc338f2..e38f92b 100644 --- a/js/packages/web/src/components/ArtContent/index.tsx +++ b/js/packages/web/src/components/ArtContent/index.tsx @@ -1,13 +1,12 @@ import React, { Ref, useCallback, useEffect, useState } from 'react'; import { Image } from 'antd'; -import { MetadataCategory, MetadataFile } from '@oyster/common'; +import { MetadataCategory, MetadataFile, pubkeyToString } from '@oyster/common'; import { MeshViewer } from '../MeshViewer'; import { ThreeDots } from '../MyLoader'; import { useCachedImage, useExtendedArt } from '../../hooks'; import { Stream, StreamPlayerApi } from '@cloudflare/stream-react'; import { PublicKey } from '@solana/web3.js'; import { getLast } from '../../utils/utils'; -import { pubkeyToString } from '../../utils/pubkeyToString'; const MeshArtContent = ({ uri, diff --git a/js/packages/web/src/hooks/useArt.ts b/js/packages/web/src/hooks/useArt.ts index a7c948e..35961a7 100644 --- a/js/packages/web/src/hooks/useArt.ts +++ b/js/packages/web/src/hooks/useArt.ts @@ -10,11 +10,11 @@ import { ParsedAccount, StringPublicKey, useLocalStorage, + pubkeyToString, } from '@oyster/common'; import { WhitelistedCreator } from '@oyster/common/dist/lib/models/metaplex/index'; import { Cache } from 'three'; import { useInView } from 'react-intersection-observer'; -import { pubkeyToString } from '../utils/pubkeyToString'; const metadataToArt = ( info: Metadata | undefined, diff --git a/js/packages/web/src/hooks/useCreator.ts b/js/packages/web/src/hooks/useCreator.ts index 5344405..d996166 100644 --- a/js/packages/web/src/hooks/useCreator.ts +++ b/js/packages/web/src/hooks/useCreator.ts @@ -1,6 +1,5 @@ -import { StringPublicKey } from '@oyster/common'; +import { StringPublicKey, pubkeyToString } from '@oyster/common'; import { useMeta } from '../contexts'; -import { pubkeyToString } from '../utils/pubkeyToString'; export const useCreator = (id?: StringPublicKey) => { const { whitelistedCreatorsByCreator } = useMeta(); diff --git a/js/packages/web/src/utils/extendCreator.ts b/js/packages/web/src/utils/extendCreator.ts new file mode 100644 index 0000000..294b542 --- /dev/null +++ b/js/packages/web/src/utils/extendCreator.ts @@ -0,0 +1,8 @@ +import { WhitelistedCreator } from '@oyster/common/dist/lib/models'; +import names from '../config/userNames.json'; + +export const extendCreatorName = (creator: WhitelistedCreator) => { + const nameInfo = (names as any)[creator.address.toBase58()]; + + return { ...creator, ...nameInfo }; +}; diff --git a/js/packages/web/src/utils/pubkeyToString.ts b/js/packages/web/src/utils/pubkeyToString.ts deleted file mode 100644 index 4c1f5eb..0000000 --- a/js/packages/web/src/utils/pubkeyToString.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { PublicKey } from '@solana/web3.js'; - -export const pubkeyToString = (key: PublicKey | string = '') => { - return typeof key === 'string' ? key : key?.toBase58() || ''; -}; From 06a10a40256f3e77cd341a23edf3018afa565e27 Mon Sep 17 00:00:00 2001 From: exromany Date: Mon, 16 Aug 2021 13:23:32 +0300 Subject: [PATCH 06/10] fix: after update master --- .../common/src/contexts/meta/loadAccounts.ts | 153 +++++++++--------- js/packages/web/src/utils/extendCreator.ts | 8 - 2 files changed, 78 insertions(+), 83 deletions(-) delete mode 100644 js/packages/web/src/utils/extendCreator.ts diff --git a/js/packages/common/src/contexts/meta/loadAccounts.ts b/js/packages/common/src/contexts/meta/loadAccounts.ts index 3878196..f79ac02 100644 --- a/js/packages/common/src/contexts/meta/loadAccounts.ts +++ b/js/packages/common/src/contexts/meta/loadAccounts.ts @@ -25,8 +25,8 @@ import { processMetaplexAccounts } from './processMetaplexAccounts'; import { processMetaData } from './processMetaData'; import { processVaultData } from './processVaultData'; import { ParsedAccount } from '../accounts/types'; -import { getMultipleAccounts } from '../accounts'; import { getEmptyMetaState } from './getEmptyMetaState'; +import { getMultipleAccounts } from '../accounts/getMultipleAccounts'; async function getProgramAccounts( connection: Connection, @@ -93,89 +93,58 @@ export const loadAccounts = async (connection: Connection, all: boolean) => { } }; - const additionalPromises: Promise[] = []; + let isSelectivePullMetadata = false; + const pullMetadata = async (creators: AccountAndPubkey[]) => { + await forEach(processMetaplexAccounts)(creators); - const IS_BIG_STORE = - process.env.NEXT_PUBLIC_BIG_STORE?.toLowerCase() === 'true'; - console.log(`Is big store: ${IS_BIG_STORE}`); + const whitelistedCreators = Object.values( + tempCache.whitelistedCreatorsByCreator, + ); - const promises = [ - getProgramAccounts(connection, VAULT_ID).then(forEach(processVaultData)), - getProgramAccounts(connection, AUCTION_ID).then(forEach(processAuctions)), - getProgramAccounts(connection, METAPLEX_ID).then( - forEach(processMetaplexAccounts), - ), - IS_BIG_STORE - ? getProgramAccounts(connection, METADATA_PROGRAM_ID).then( + if (whitelistedCreators.length > 3) { + console.log(' too many creators, pulling all nfts in one go'); + additionalPromises.push( + getProgramAccounts(connection, METADATA_PROGRAM_ID).then( forEach(processMetaData), - ) - : undefined, - getProgramAccounts(connection, METAPLEX_ID, { - filters: [ - { - dataSize: MAX_WHITELISTED_CREATOR_SIZE, - }, - ], - }).then(async creators => { - const result = await forEach(processMetaplexAccounts)(creators); - - if (IS_BIG_STORE) { - return result; - } - - const whitelistedCreators = Object.values( - tempCache.whitelistedCreatorsByCreator, + ), ); + } else { + console.log('pulling optimized nfts'); + isSelectivePullMetadata = true; - if (whitelistedCreators.length > 3) { - console.log(' too many creators, pulling all nfts in one go'); - additionalPromises.push( - getProgramAccounts(connection, METADATA_PROGRAM_ID).then( - forEach(processMetaData), - ), - ); - } else { - console.log('pulling optimized nfts'); - - for (let i = 0; i < MAX_CREATOR_LIMIT; i++) { - for (let j = 0; j < whitelistedCreators.length; j++) { - additionalPromises.push( - getProgramAccounts(connection, METADATA_PROGRAM_ID, { - filters: [ - { - memcmp: { - offset: - 1 + // key - 32 + // update auth - 32 + // mint - 4 + // name string length - MAX_NAME_LENGTH + // name - 4 + // uri string length - MAX_URI_LENGTH + // uri - 4 + // symbol string length - MAX_SYMBOL_LENGTH + // symbol - 2 + // seller fee basis points - 1 + // whether or not there is a creators vec - 4 + // creators vec length - i * MAX_CREATOR_LEN, - bytes: whitelistedCreators[j].info.address, - }, + for (let i = 0; i < MAX_CREATOR_LIMIT; i++) { + for (let j = 0; j < whitelistedCreators.length; j++) { + additionalPromises.push( + getProgramAccounts(connection, METADATA_PROGRAM_ID, { + filters: [ + { + memcmp: { + offset: + 1 + // key + 32 + // update auth + 32 + // mint + 4 + // name string length + MAX_NAME_LENGTH + // name + 4 + // uri string length + MAX_URI_LENGTH + // uri + 4 + // symbol string length + MAX_SYMBOL_LENGTH + // symbol + 2 + // seller fee basis points + 1 + // whether or not there is a creators vec + 4 + // creators vec length + i * MAX_CREATOR_LEN, + bytes: whitelistedCreators[j].info.address, }, - ], - }).then(forEach(processMetaData)), - ); - } + }, + ], + }).then(forEach(processMetaData)), + ); } } - }), - ]; - await Promise.all(promises); - await Promise.all(additionalPromises); + } + }; - await postProcessMetadata(tempCache, all); - console.log('Metadata size', tempCache.metadata.length); - - if (additionalPromises.length > 0) { + const pullEditions = async () => { console.log('Pulling editions for optimized metadata'); let setOf100MetadataEditionKeys: string[] = []; const editionPromises: Promise<{ @@ -241,6 +210,39 @@ export const loadAccounts = async (connection: Connection, all: boolean) => { Object.keys(tempCache.editions).length, Object.keys(tempCache.masterEditions).length, ); + }; + + const IS_BIG_STORE = + all || process.env.NEXT_PUBLIC_BIG_STORE?.toLowerCase() === 'true'; + console.log(`Is big store: ${IS_BIG_STORE}`); + + const additionalPromises: Promise[] = []; + const basePromises = [ + getProgramAccounts(connection, VAULT_ID).then(forEach(processVaultData)), + getProgramAccounts(connection, AUCTION_ID).then(forEach(processAuctions)), + getProgramAccounts(connection, METAPLEX_ID).then( + forEach(processMetaplexAccounts), + ), + IS_BIG_STORE + ? getProgramAccounts(connection, METADATA_PROGRAM_ID).then( + forEach(processMetaData), + ) + : getProgramAccounts(connection, METAPLEX_ID, { + filters: [ + { + dataSize: MAX_WHITELISTED_CREATOR_SIZE, + }, + ], + }).then(pullMetadata), + ]; + await Promise.all(basePromises); + await Promise.all(additionalPromises); + + await postProcessMetadata(tempCache, all); + console.log('Metadata size', tempCache.metadata.length); + + if (isSelectivePullMetadata) { + await pullEditions(); } return tempCache; @@ -272,6 +274,7 @@ export const metadataByMintUpdater = async ( ) => { const key = metadata.info.mint; if ( + all || isMetadataPartOfStore( metadata, state.store, diff --git a/js/packages/web/src/utils/extendCreator.ts b/js/packages/web/src/utils/extendCreator.ts deleted file mode 100644 index 294b542..0000000 --- a/js/packages/web/src/utils/extendCreator.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { WhitelistedCreator } from '@oyster/common/dist/lib/models'; -import names from '../config/userNames.json'; - -export const extendCreatorName = (creator: WhitelistedCreator) => { - const nameInfo = (names as any)[creator.address.toBase58()]; - - return { ...creator, ...nameInfo }; -}; From e9841d4bb121fbea784ff60c83ddd3bb1a26d220 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 13 Sep 2021 22:26:13 -0400 Subject: [PATCH 07/10] cli: add support for custom spl-token mints (#388) --- js/packages/cli/package.json | 1 + js/packages/cli/src/cli.ts | 56 ++++++++++++++++++++++---- js/packages/cli/src/commands/mint.ts | 31 +++++++++++++- js/packages/cli/src/helpers/various.ts | 4 +- js/yarn.lock | 14 ++++++- 5 files changed, 94 insertions(+), 12 deletions(-) diff --git a/js/packages/cli/package.json b/js/packages/cli/package.json index 68e9cd6..4bbd006 100644 --- a/js/packages/cli/package.json +++ b/js/packages/cli/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@project-serum/anchor": "^0.13.2", + "@solana/spl-token": "^0.1.8", "arweave": "^1.10.16", "bn.js": "^5.2.0", "commander": "^8.1.0", diff --git a/js/packages/cli/src/cli.ts b/js/packages/cli/src/cli.ts index 4241d32..3aae533 100755 --- a/js/packages/cli/src/cli.ts +++ b/js/packages/cli/src/cli.ts @@ -6,6 +6,7 @@ import * as anchor from '@project-serum/anchor'; import BN from 'bn.js'; import { fromUTF8Array, parsePrice } from './helpers/various'; +import { Token, TOKEN_PROGRAM_ID } from '@solana/spl-token'; import { PublicKey } from '@solana/web3.js'; import { CACHE_PATH, CONFIG_ARRAY_START, CONFIG_LINE_SIZE, EXTENSION_JSON, EXTENSION_PNG, } from './helpers/constants'; import { getCandyMachineAddress, loadAnchorProgram, loadWalletKey, } from './helpers/accounts'; @@ -144,16 +145,54 @@ programCommand('verify') }); programCommand('create_candy_machine') - .option('-p, --price ', 'SOL price', '1') + .option('-p, --price ', 'Price denominated in SOL or spl-token override', '1') + .option('-t, --spl-token ', 'SPL token used to price NFT mint. To use SOL leave this empty.') + .option('-t, --spl-token-account ', 'SPL token account that receives mint payments. Only required if spl-token is specified.') .action(async (directory, cmd) => { - const { keypair, env, price, cacheName } = cmd.opts(); + const { keypair, env, price, cacheName, splToken, splTokenAccount } = cmd.opts(); - const lamports = parsePrice(price); + let parsedPrice = parsePrice(price); const cacheContent = loadCache(cacheName, env); const walletKeyPair = loadWalletKey(keypair); const anchorProgram = await loadAnchorProgram(walletKeyPair, env); + let wallet = walletKeyPair.publicKey; + const remainingAccounts = []; + if (splToken || splTokenAccount) { + if (!splToken) { + throw new Error("If spl-token-account is set, spl-token must also be set") + } + const splTokenKey = new PublicKey(splToken); + const splTokenAccountKey = new PublicKey(splTokenAccount); + if (!splTokenAccount) { + throw new Error("If spl-token is set, spl-token-account must also be set") + } + + const token = new Token( + anchorProgram.provider.connection, + splTokenKey, + TOKEN_PROGRAM_ID, + walletKeyPair + ); + + const mintInfo = await token.getMintInfo(); + if (!mintInfo.isInitialized) { + throw new Error(`The specified spl-token is not initialized`); + } + const tokenAccount = await token.getAccountInfo(splTokenAccountKey); + if (!tokenAccount.isInitialized) { + throw new Error(`The specified spl-token-account is not initialized`); + } + if (!tokenAccount.mint.equals(splTokenKey)) { + throw new Error(`The spl-token-account's mint (${tokenAccount.mint.toString()}) does not match specified spl-token ${splTokenKey.toString()}`); + } + + wallet = splTokenAccountKey; + parsedPrice = parsePrice(price, 10 ** mintInfo.decimals); + remainingAccounts.push({ pubkey: splTokenKey, isWritable: false, isSigner: false }); + } + const config = new PublicKey(cacheContent.program.config); const [candyMachine, bump] = await getCandyMachineAddress( config, @@ -163,14 +202,14 @@ programCommand('create_candy_machine') bump, { uuid: cacheContent.program.uuid, - price: new anchor.BN(lamports), + price: new anchor.BN(parsedPrice), itemsAvailable: new anchor.BN(Object.keys(cacheContent.items).length), goLiveDate: null, }, { accounts: { candyMachine, - wallet: walletKeyPair.publicKey, + wallet, config: config, authority: walletKeyPair.publicKey, payer: walletKeyPair.publicKey, @@ -178,6 +217,7 @@ programCommand('create_candy_machine') rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, signers: [], + remainingAccounts, }, ); @@ -215,12 +255,14 @@ programCommand('set_start_date') }); programCommand('mint_one_token') + .option('-t, --spl-token-account ', 'SPL token account to payfrom') .action(async (directory, cmd) => { - const {keypair, env, cacheName} = cmd.opts(); + const {keypair, env, cacheName, splTokenAccount} = cmd.opts(); const cacheContent = loadCache(cacheName, env); const configAddress = new PublicKey(cacheContent.program.config); - const tx = await mint(keypair, env, configAddress); + const splTokenAccountKey = splTokenAccount ? new PublicKey(splTokenAccount) : undefined; + const tx = await mint(keypair, env, configAddress, splTokenAccountKey); log.info('Done', tx); }); diff --git a/js/packages/cli/src/commands/mint.ts b/js/packages/cli/src/commands/mint.ts index 533c0cb..711936d 100644 --- a/js/packages/cli/src/commands/mint.ts +++ b/js/packages/cli/src/commands/mint.ts @@ -13,7 +13,7 @@ import * as anchor from "@project-serum/anchor"; import { MintLayout, Token } from "@solana/spl-token"; import { createAssociatedTokenAccountInstruction } from "../helpers/instructions"; -export async function mint(keypair: string, env: string, configAddress: PublicKey): Promise { +export async function mint(keypair: string, env: string, configAddress: PublicKey, splTokenAccountKey?: PublicKey): Promise { const mint = Keypair.generate(); const userKeyPair = loadWalletKey(keypair); @@ -28,10 +28,36 @@ export async function mint(keypair: string, env: string, configAddress: PublicKe configAddress, uuid, ); - const candyMachine = await anchorProgram.account.candyMachine.fetch( + const candyMachine : any = await anchorProgram.account.candyMachine.fetch( candyMachineAddress, ); + const remainingAccounts = []; + if (splTokenAccountKey) { + const candyMachineTokenMintKey = candyMachine.tokenMint; + if (!candyMachineTokenMintKey) { + throw new Error('Candy machine data does not have token mint configured. Can\'t use spl-token-account'); + } + const token = new Token( + anchorProgram.provider.connection, + candyMachine.tokenMint, + TOKEN_PROGRAM_ID, + userKeyPair + ); + + const tokenAccount = await token.getAccountInfo(splTokenAccountKey); + if (!candyMachine.tokenMint.equals(tokenAccount.mint)) { + throw new Error(`Specified spl-token-account's mint (${tokenAccount.mint.toString()}) does not match candy machine's token mint (${candyMachine.tokenMint.toString()})`); + } + + if (!tokenAccount.owner.equals(userKeyPair.publicKey)) { + throw new Error(`Specified spl-token-account's owner (${tokenAccount.owner.toString()}) does not match user public key (${userKeyPair.publicKey})`); + } + + remainingAccounts.push({ pubkey: splTokenAccountKey, isWritable: true, isSigner: false }); + remainingAccounts.push({ pubkey: userKeyPair.publicKey, isWritable: false, isSigner: true }); + } + const metadataAddress = await getMetadata(mint.publicKey); const masterEdition = await getMasterEdition(mint.publicKey); return await anchorProgram.rpc.mintNft({ @@ -53,6 +79,7 @@ export async function mint(keypair: string, env: string, configAddress: PublicKe clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, }, signers: [mint, userKeyPair], + remainingAccounts, instructions: [ anchor.web3.SystemProgram.createAccount({ fromPubkey: userKeyPair.publicKey, diff --git a/js/packages/cli/src/helpers/various.ts b/js/packages/cli/src/helpers/various.ts index 91d3a0f..0843fbb 100644 --- a/js/packages/cli/src/helpers/various.ts +++ b/js/packages/cli/src/helpers/various.ts @@ -48,6 +48,6 @@ export function fromUTF8Array(data: number[]) { return str; } -export function parsePrice(price) { - return Math.ceil(parseFloat(price) * LAMPORTS_PER_SOL); +export function parsePrice(price: string, mantissa: number = LAMPORTS_PER_SOL) { + return Math.ceil(parseFloat(price) * mantissa); } diff --git a/js/yarn.lock b/js/yarn.lock index 018bfe3..cf63c1b 100644 --- a/js/yarn.lock +++ b/js/yarn.lock @@ -1818,6 +1818,18 @@ buffer-layout "^1.2.0" dotenv "10.0.0" +"@solana/spl-token@^0.1.8": + version "0.1.8" + resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6" + integrity sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ== + dependencies: + "@babel/runtime" "^7.10.5" + "@solana/web3.js" "^1.21.0" + bn.js "^5.1.0" + buffer "6.0.3" + buffer-layout "^1.2.0" + dotenv "10.0.0" + "@solana/wallet-adapter-base@^0.4.1": version "0.4.1" resolved "https://registry.yarnpkg.com/@solana/wallet-adapter-base/-/wallet-adapter-base-0.4.1.tgz#3264220c7eef5abaf7eca0e77cc51403f4f0fcf6" @@ -7981,7 +7993,7 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" -loglevel@^1.6.8: +loglevel@^1.6.8, loglevel@^1.7.1: version "1.7.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== From 756f58ecd66fe7b8c0efaf47d66050bdb4528def Mon Sep 17 00:00:00 2001 From: grimAgent <90475821+grimAgent@users.noreply.github.com> Date: Tue, 14 Sep 2021 15:54:51 -0400 Subject: [PATCH 08/10] Command to verify candymachine price (#397) --- js/packages/cli/src/cli.ts | 45 ++++++++++++++++++++++++++++ js/packages/cli/src/helpers/cache.ts | 12 ++++---- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/js/packages/cli/src/cli.ts b/js/packages/cli/src/cli.ts index 3aae533..1de61f3 100755 --- a/js/packages/cli/src/cli.ts +++ b/js/packages/cli/src/cli.ts @@ -144,6 +144,51 @@ programCommand('verify') saveCache(cacheName, env, cacheContent); }); +programCommand('verify_price') + .option('-p, --price ') + .option('--cache-path ') + .action(async (directory, cmd) => { + const { keypair, env, price, cacheName, cachePath } = cmd.opts(); + const lamports = parsePrice(price); + + if (isNaN(lamports)) { + return log.error(`verify_price requires a --price to be set`); + } + + log.info(`Expected price is: ${lamports}`); + + const cacheContent = loadCache(cacheName, env, cachePath); + + if (!cacheContent) { + return log.error( + `No cache found, can't continue. Make sure you are in the correct directory where the assets are located or use the --cache-path option.`, + ); + } + + const walletKeyPair = loadWalletKey(keypair); + const anchorProgram = await loadAnchorProgram(walletKeyPair, env); + + const [candyMachine] = await getCandyMachineAddress( + new PublicKey(cacheContent.program.config), + cacheContent.program.uuid, + ); + + const machine = await anchorProgram.account.candyMachine.fetch( + candyMachine, + ); + + //@ts-ignore + const candyMachineLamports = machine.data.price.toNumber(); + + log.info(`Candymachine price is: ${candyMachineLamports}`); + + if (lamports != candyMachineLamports) { + throw new Error(`Expected price and CandyMachine's price do not match!`); + } + + log.info(`Good to go!`); + }); + programCommand('create_candy_machine') .option('-p, --price ', 'Price denominated in SOL or spl-token override', '1') .option('-t, --spl-token ', 'SPL token used to price NFT mint. To use SOL leave this empty.') diff --git a/js/packages/cli/src/helpers/cache.ts b/js/packages/cli/src/helpers/cache.ts index 5a55825..cf1fc66 100644 --- a/js/packages/cli/src/helpers/cache.ts +++ b/js/packages/cli/src/helpers/cache.ts @@ -2,17 +2,17 @@ import path from "path"; import { CACHE_PATH } from "./constants"; import fs from "fs"; -export function cachePath(env: string, cacheName: string) { - return path.join(CACHE_PATH, `${env}-${cacheName}`); +export function cachePath(env: string, cacheName: string, cPath: string = CACHE_PATH) { + return path.join(cPath, `${env}-${cacheName}`); } -export function loadCache(cacheName: string, env: string) { - const path = cachePath(env, cacheName); +export function loadCache(cacheName: string, env: string, cPath: string = CACHE_PATH) { + const path = cachePath(env, cacheName, cPath); return fs.existsSync(path) ? JSON.parse(fs.readFileSync(path).toString()) : undefined; } -export function saveCache(cacheName: string, env: string, cacheContent) { - fs.writeFileSync(cachePath(env, cacheName), JSON.stringify(cacheContent)); +export function saveCache(cacheName: string, env: string, cacheContent, cPath: string = CACHE_PATH) { + fs.writeFileSync(cachePath(env, cacheName, cPath), JSON.stringify(cacheContent)); } From b47912232e94981e4e73fb952bd1f871cee038d2 Mon Sep 17 00:00:00 2001 From: "potato.sol" <81876372+solserer-labs@users.noreply.github.com> Date: Tue, 14 Sep 2021 21:56:59 +0200 Subject: [PATCH 09/10] Candy machine post sale mass signer (#365) * candy_signer * sign docs * some docs * Add a lil vid * cleaning * clean * docs video and merge with refactor * move to cache * refactor to cache, move to commands * Update README.md Co-authored-by: potato --- js/packages/cli/README.md | 78 +++++++-- js/packages/cli/package.json | 2 + js/packages/cli/src/cli.ts | 67 ++++--- js/packages/cli/src/commands/signAll.ts | 213 +++++++++++++++++++++++ js/packages/cli/src/helpers/cache.ts | 1 + js/packages/cli/src/helpers/constants.ts | 7 +- js/packages/cli/src/helpers/various.ts | 18 +- js/packages/cli/src/types.ts | 213 ++++++++++++++++++++++- 8 files changed, 555 insertions(+), 44 deletions(-) create mode 100644 js/packages/cli/src/commands/signAll.ts diff --git a/js/packages/cli/README.md b/js/packages/cli/README.md index 550e234..a419665 100644 --- a/js/packages/cli/README.md +++ b/js/packages/cli/README.md @@ -1,21 +1,9 @@ -# Candy Machine! +# CANDY MACHINE -Install dependencies -``` -yarn -``` - - -## usage -```shell -metaplex upload /path/to/assets -e devnet --keypair /path/to/admin-payer -n 3 -metaplex create_candy_machine -e devnet --keypair /path/to/admin-payer --price 1.5 -metaplex set_start_date -e devnet --keypair /path/to/admin-payer -metaplex mint_one_token -e devnet --keypair /path/to/user-payer -``` +https://user-images.githubusercontent.com/81876372/133098938-dc2c91a6-1280-4ee1-bf0e-db0ccc972ff7.mp4 ## assets folder -* Folder with file pairs named from with growing integer numbers starting from 0.png and 0.json +* Folder with file pairs named with inrementing integer numbers starting from 0.png and 0.json * the image HAS TO be a `PNG` * JSON format can be checked out here: https://docs.metaplex.com/nft-standard. example below: ```json @@ -71,3 +59,63 @@ metaplex mint_one_token -e devnet --keypair /path/to/user-payer } } ``` + +Install and build +``` +yarn install +yarn build +yarn run package:linuxb +OR +yarn run package:linux +OR +yarn run package:macos +``` + +You can now either use `metaplex` OR the `ts-node cli` to execute the following commands. + +1. Upload your images and metadata. Refer to the NFT [standard](https://docs.metaplex.com/nft-standard) for the correct format. +``` +metaplex upload ~/nft-test/mini_drop --keypair ~/.config/solana/id.json +ts-node cli upload ~/nft-test/mini_drop --keypair ~/.config/solana/id.json +``` + +2. Verify everything is uploaded. Rerun the first command until it is. +``` +metaplex verify --keypair ~/.config/solana/id.json +ts-node cli verify --keypair ~/.config/solana/id.json +``` + +3. Create your candy machine. It can cost up to ~15 solana per 10,000 images. +``` +metaplex create_candy_machine -k ~/.config/solana/id.json -p 1 +ts-node cli create_candy_machine -k ~/.config/solana/id.json -p 3 +``` + +4. Set the start date for your candy machine. +``` +metaplex set_start_date -k ~/.config/solana/id.json -d "20 Apr 2021 04:20:00 GMT" +ts-node cli set_start_date -k ~/.config/solana/id.json -d "20 Apr 2021 04:20:00 GMT" +``` + +5. Test mint a token (provided it's after the start date) +``` +metaplex mint_one_token -k ~/.config/solana/id.json +ts-node cli mint_one_token -k ~/.config/solana/id.json +``` + +6. Check if you received any tokens. +``` +spl-token accounts +``` + +7. If you are listed as a creator, run this command to sign your NFTs post sale. This will sign only the latest candy machine that you've created (stored in .cache/candyMachineList.json). +``` +metaplex sign_candy_machine_metadata -k ~/.config/solana/id.json +ts-node cli sign_candy_machine_metadata -k ~/.config/solana/id.json +``` + +8. If you wish to sign metadata from another candy machine run with the --cndy flag. +``` +metaplex sign_candy_machine_metadata -k ~/.config/solana/id.json --cndy CANDY_MACHINE_ADDRESS_HERE +ts-node cli sign_candy_machine_metadata -k ~/.config/solana/id.json --cndy CANDY_MACHINE_ADDRESS_HERE + diff --git a/js/packages/cli/package.json b/js/packages/cli/package.json index 4bbd006..ce4f576 100644 --- a/js/packages/cli/package.json +++ b/js/packages/cli/package.json @@ -10,6 +10,7 @@ "build": "tsc -p ./src", "watch": "tsc -w -p ./src", "package:linux": "pkg . --no-bytecode --targets node14-linux-x64 --output bin/linux/metaplex", + "package:linuxb": "pkg . --targets node14-linux-x64 --output bin/linux/metaplex", "package:macos": "pkg . --no-bytecode --targets node14-macos-x64 --output bin/macos/metaplex", "format": "prettier --loglevel warn --write \"**/*.{ts,js,json,yaml}\"", "format:check": "prettier --loglevel warn --check \"**/*.{ts,js,json,yaml}\"", @@ -24,6 +25,7 @@ "@solana/spl-token": "^0.1.8", "arweave": "^1.10.16", "bn.js": "^5.2.0", + "borsh": "^0.4.0", "commander": "^8.1.0", "form-data": "^4.0.0", "loglevel": "^1.7.1", diff --git a/js/packages/cli/src/cli.ts b/js/packages/cli/src/cli.ts index 1de61f3..96bb023 100755 --- a/js/packages/cli/src/cli.ts +++ b/js/packages/cli/src/cli.ts @@ -1,4 +1,3 @@ -#!/usr/bin/env ts-node import * as fs from 'fs'; import * as path from 'path'; import { program } from 'commander'; @@ -14,7 +13,8 @@ import { Config } from './types'; import { upload } from './commands/upload'; import { loadCache, saveCache } from './helpers/cache'; import { mint } from "./commands/mint"; -import { signAllUnapprovedMetadata, signMetadata } from "./commands/sign"; +import { signMetadata } from "./commands/sign"; +import { signAllMetadataFromCandyMachine } from "./commands/signAll"; import log from 'loglevel'; program.version('0.0.1'); @@ -59,18 +59,21 @@ programCommand('upload') const startMs = Date.now(); log.info("started at: " + startMs.toString()) + let warn = false; for (; ;) { const successful = await upload(files, cacheName, env, keypair, elemCount); if (successful) { + warn = false; break; } else { + warn = true; log.warn("upload was not successful, rerunning"); } } const endMs = Date.now(); const timeTaken = new Date(endMs - startMs).toISOString().substr(11, 8); log.info(`ended at: ${new Date(endMs).toString()}. time taken: ${timeTaken}`) - + if (warn) { log.info("not all images have been uplaoded, rerun this step.") } }); programCommand('verify') @@ -100,7 +103,7 @@ programCommand('verify') const cacheItem = cacheContent.items[key]; if (!name.match(cacheItem.name) || !uri.match(cacheItem.link)) { //leaving here for debugging reasons, but it's pretty useless. if the first upload fails - all others are wrong - // console.log( + // log.info( // `Name (${name}) or uri (${uri}) didnt match cache values of (${cacheItem.name})` + // `and (${cacheItem.link}). marking to rerun for image`, // key, @@ -127,14 +130,12 @@ programCommand('verify') const lineCount = new BN(config.data.slice(247, 247 + 4), undefined, 'le'); log.info( - `uploaded (${lineCount.toNumber()}) out of (${ - configData.data.maxNumberOfLines + `uploaded (${lineCount.toNumber()}) out of (${configData.data.maxNumberOfLines })`, ); if (configData.data.maxNumberOfLines > lineCount.toNumber()) { throw new Error( - `predefined number of NFTs (${ - configData.data.maxNumberOfLines + `predefined number of NFTs (${configData.data.maxNumberOfLines }) is smaller than the uploaded one (${lineCount.toNumber()})`, ); } else { @@ -265,7 +266,6 @@ programCommand('create_candy_machine') remainingAccounts, }, ); - saveCache(cacheName, env, cacheContent); log.info(`create_candy_machine finished. candy machine pubkey: ${candyMachine.toBase58()}`); }); @@ -316,7 +316,7 @@ programCommand('sign') // eslint-disable-next-line @typescript-eslint/no-unused-vars .option('-m, --metadata ', 'base58 metadata account id') .action(async (directory, cmd) => { - const {keypair, env, metadata} = cmd.opts(); + const { keypair, env, metadata } = cmd.opts(); await signMetadata( metadata, @@ -325,17 +325,6 @@ programCommand('sign') ); }); -programCommand('sign_all') - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .action(async (directory, cmd) => { - const {keypair, env} = cmd.opts(); - - await signAllUnapprovedMetadata( - keypair, - env - ); - }); - function programCommand(name: string) { return program .command(name) @@ -355,14 +344,44 @@ function programCommand(name: string) { // eslint-disable-next-line @typescript-eslint/no-unused-vars function setLogLevel(value, prev) { - if (value === undefined || value === null){ + if (value === undefined || value === null) { return } log.info("setting the log value to: " + value); log.setLevel(value); } -program.command('find-wallets').action(() => { -}); +programCommand("sign_candy_machine_metadata") + .option('-cndy, --candy-address ', 'Candy machine address', '') + .option('-b, --batch-size ', 'Batch size', '10') + .action(async (directory, cmd) => { + let { keypair, env, cacheName, candyAddress, batchSize } = cmd.opts(); + if (!keypair || keypair == '') { + log.info("Keypair required!"); + return; + } + if (!candyAddress || candyAddress == '') { + log.info("Candy machine address required! Using from saved list.") + const cacheContent = loadCache(cacheName, env); + const config = new PublicKey(cacheContent.program.config); + const [candyMachine, bump] = await getCandyMachineAddress( + config, + cacheContent.program.uuid, + ); + candyAddress = candyMachine.toBase58(); + } + let batchSizeParsed = parseInt(batchSize) + if (!parseInt(batchSize)) { + log.info("Batch size needs to be an integer!") + return; + } + const walletKeyPair = loadWalletKey(keypair); + const anchorProgram = await loadAnchorProgram(walletKeyPair, env); + log.info("Creator pubkey: ", walletKeyPair.publicKey.toBase58()) + log.info("Environment: ", env) + log.info("Candy machine address: ", candyAddress) + log.info("Batch Size: ", batchSizeParsed) + await signAllMetadataFromCandyMachine(anchorProgram.provider.connection, walletKeyPair, candyAddress, batchSizeParsed) + }); program.parse(process.argv); diff --git a/js/packages/cli/src/commands/signAll.ts b/js/packages/cli/src/commands/signAll.ts new file mode 100644 index 0000000..8524d82 --- /dev/null +++ b/js/packages/cli/src/commands/signAll.ts @@ -0,0 +1,213 @@ +import { Keypair, PublicKey, TransactionInstruction, Connection, AccountInfo } from '@solana/web3.js'; +import { sendTransactionWithRetryWithKeypair } from '../helpers/transactions'; +import * as borsh from "borsh" +import { + MAX_NAME_LENGTH, + MAX_URI_LENGTH, + MAX_SYMBOL_LENGTH, + MAX_CREATOR_LEN, + TOKEN_METADATA_PROGRAM_ID, +} from '../helpers/constants'; +import { + AccountAndPubkey, + Metadata, + METADATA_SCHEMA +} from '../types' +/* + Get accounts by candy machine creator address + Get only verified ones + Get only unverified ones with creator address + Grab n at a time and batch sign and send transaction + + PS: Don't sign candy machine addresses that you do not know about. Signing verifies your participation. +*/ +async function decodeMetadata(buffer) { + const metadata = borsh.deserializeUnchecked(METADATA_SCHEMA, Metadata, buffer); + return metadata; +}; + +async function getProgramAccounts( + connection: Connection, + programId: String, + configOrCommitment?: any, +): Promise> { + const extra: any = {}; + let commitment; + //let encoding; + + if (configOrCommitment) { + if (typeof configOrCommitment === 'string') { + commitment = configOrCommitment; + } else { + commitment = configOrCommitment.commitment; + //encoding = configOrCommitment.encoding; + + if (configOrCommitment.dataSlice) { + extra.dataSlice = configOrCommitment.dataSlice; + } + + if (configOrCommitment.filters) { + extra.filters = configOrCommitment.filters; + } + } + } + + const args = connection._buildArgs([programId], commitment, 'base64', extra); + const unsafeRes = await (connection as any)._rpcRequest( + 'getProgramAccounts', + args, + ); + //console.log(unsafeRes) + const data = ( + unsafeRes.result as Array<{ + account: AccountInfo<[string, string]>; + pubkey: string; + }> + ).map(item => { + return { + account: { + // TODO: possible delay parsing could be added here + data: Buffer.from(item.account.data[0], 'base64'), + executable: item.account.executable, + lamports: item.account.lamports, + // TODO: maybe we can do it in lazy way? or just use string + owner: item.account.owner, + } as AccountInfo, + pubkey: item.pubkey, + }; + }); + + return data; +} + +export async function signAllMetadataFromCandyMachine( + connection, + wallet, + candyMachineAddress, + batchSize + ){ + let metadataByCandyMachine = await getAccountsByCreatorAddress(candyMachineAddress, connection) + console.log(`Found ${metadataByCandyMachine.length} nft's minted by candy machine ${candyMachineAddress}`) + let candyVerifiedListToSign = await getCandyMachineVerifiedMetadata(metadataByCandyMachine, candyMachineAddress, wallet.publicKey.toBase58()) + console.log(`Found ${candyVerifiedListToSign.length} nft's to sign by ${wallet.publicKey.toBase58()}`) + await sendSignMetadata(connection, wallet, candyVerifiedListToSign, batchSize) +} + +async function getAccountsByCreatorAddress(creatorAddress, connection) { + let metadataAccounts = await getProgramAccounts(connection, TOKEN_METADATA_PROGRAM_ID.toBase58(), { + filters: [ + { + memcmp: { + offset: + 1 + // key + 32 + // update auth + 32 + // mint + 4 + // name string length + MAX_NAME_LENGTH + // name + 4 + // uri string length + MAX_URI_LENGTH + // uri* + 4 + // symbol string length + MAX_SYMBOL_LENGTH + // symbol + 2 + // seller fee basis points + 1 + // whether or not there is a creators vec + 4 + // creators vec length + 0 * MAX_CREATOR_LEN, + bytes: creatorAddress, + }, + }, + ], + }) + let decodedAccounts = [] + for (let i = 0; i < metadataAccounts.length; i++) { + let e = metadataAccounts[i]; + let decoded = await decodeMetadata(e.account.data) + let accountPubkey = e.pubkey + let store = [decoded, accountPubkey] + decodedAccounts.push(store) + } + return decodedAccounts +} + +async function getCandyMachineVerifiedMetadata(metadataList, candyAddress, creatorAddress){ + let verifiedList = []; + metadataList.forEach(meta => { + let verifiedCandy = false; + let verifiedCreator = true; + meta[0].data.creators.forEach(creator => { + if (new PublicKey(creator.address).toBase58() == candyAddress && creator.verified === 1) { + verifiedCandy = true; + } + if (new PublicKey(creator.address).toBase58() == creatorAddress && creator.verified === 0) { + verifiedCreator = false; + } + }); + if(verifiedCandy && !verifiedCreator){ + verifiedList.push(meta) + } + }); + return verifiedList +} + +async function sendSignMetadata( + connection, + wallet, + metadataList, + batchsize +) { + let total = 0; + while(metadataList.length > 0){ + console.log("Signing metadata") + let sliceAmount = batchsize; + if (metadataList.length < batchsize) { + sliceAmount = metadataList.length; + } + var removed = metadataList.splice(0,sliceAmount); + total += sliceAmount; + await delay(500) + await signMetadataBatch(removed, connection, wallet) + console.log(`Processed ${total} nfts`) + } + console.log("Finished signing metadata..") +} + +async function signMetadataBatch(metadataList, connection, keypair){ + + const signers: Keypair[] = []; + const instructions: TransactionInstruction[] = []; + for (let i = 0; i < metadataList.length; i++) { + const meta = metadataList[i]; + await signMetadataSingle(meta[1], keypair.publicKey.toBase58(), instructions) + } + await sendTransactionWithRetryWithKeypair(connection, keypair, instructions, [], 'single') +} + +async function signMetadataSingle( + metadata, + creator, + instructions, +) { + const data = Buffer.from([7]); + const keys = [ + { + pubkey: new PublicKey(metadata), + isSigner: false, + isWritable: true, + }, + { + pubkey: new PublicKey(creator), + isSigner: true, + isWritable: false, + }, + ]; + instructions.push( + ({ + keys, + programId: TOKEN_METADATA_PROGRAM_ID.toBase58(), + data, + }), + ); +} + +function delay(ms: number) { + return new Promise( resolve => setTimeout(resolve, ms) ); +} diff --git a/js/packages/cli/src/helpers/cache.ts b/js/packages/cli/src/helpers/cache.ts index cf1fc66..9f2be51 100644 --- a/js/packages/cli/src/helpers/cache.ts +++ b/js/packages/cli/src/helpers/cache.ts @@ -16,3 +16,4 @@ export function loadCache(cacheName: string, env: string, cPath: string = CACHE_ export function saveCache(cacheName: string, env: string, cacheContent, cPath: string = CACHE_PATH) { fs.writeFileSync(cachePath(env, cacheName, cPath), JSON.stringify(cacheContent)); } + diff --git a/js/packages/cli/src/helpers/constants.ts b/js/packages/cli/src/helpers/constants.ts index 45cc663..7600f97 100644 --- a/js/packages/cli/src/helpers/constants.ts +++ b/js/packages/cli/src/helpers/constants.ts @@ -1,13 +1,14 @@ import { PublicKey } from '@solana/web3.js'; - export const CANDY_MACHINE = 'candy_machine'; - +export const MAX_NAME_LENGTH = 32; +export const MAX_URI_LENGTH = 200; +export const MAX_SYMBOL_LENGTH = 10; +export const MAX_CREATOR_LEN = 32 + 1 + 1; export const ARWEAVE_PAYMENT_WALLET = new PublicKey('HvwC9QSAzvGXhhVrgPmauVwFWcYZhne3hVot9EbHuFTm'); export const CANDY_MACHINE_PROGRAM_ID = new PublicKey('cndyAnrLdpjq1Ssp1z8xxDsB8dxe7u4HL5Nxi2K5WXZ'); export const TOKEN_METADATA_PROGRAM_ID = new PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'); export const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID = new PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'); export const TOKEN_PROGRAM_ID = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'); - export const CONFIG_ARRAY_START = 32 + // authority 4 + diff --git a/js/packages/cli/src/helpers/various.ts b/js/packages/cli/src/helpers/various.ts index 0843fbb..cf9b8b5 100644 --- a/js/packages/cli/src/helpers/various.ts +++ b/js/packages/cli/src/helpers/various.ts @@ -1,5 +1,7 @@ import { LAMPORTS_PER_SOL } from '@solana/web3.js'; - +import path from "path"; +import { CACHE_PATH } from "./constants"; +import fs from "fs"; export const getUnixTs = () => { return new Date().getTime() / 1000; }; @@ -51,3 +53,17 @@ export function fromUTF8Array(data: number[]) { export function parsePrice(price: string, mantissa: number = LAMPORTS_PER_SOL) { return Math.ceil(parseFloat(price) * mantissa); } + +export async function upload(data: FormData, manifest, index) { + console.log(`trying to upload ${index}.png: ${manifest.name}`); + return await ( + await fetch( + 'https://us-central1-principal-lane-200702.cloudfunctions.net/uploadFile4', + { + method: 'POST', + // @ts-ignore + body: data, + }, + ) + ).json(); +} diff --git a/js/packages/cli/src/types.ts b/js/packages/cli/src/types.ts index 4c62ae0..2de2083 100644 --- a/js/packages/cli/src/types.ts +++ b/js/packages/cli/src/types.ts @@ -1,5 +1,5 @@ import { BN } from "@project-serum/anchor"; -import { PublicKey } from "@solana/web3.js"; +import { PublicKey, AccountInfo } from "@solana/web3.js"; export class Creator { address: PublicKey; @@ -55,3 +55,214 @@ export class ConfigData { this.retainAuthority = args.retainAuthority; } } + +export type AccountAndPubkey = { + pubkey: string; + account: AccountInfo; +}; + +export enum MetadataKey { + Uninitialized = 0, + MetadataV1 = 4, + EditionV1 = 1, + MasterEditionV1 = 2, + MasterEditionV2 = 6, + EditionMarker = 7 +} + +export class MasterEditionV1 { + key: MetadataKey; + supply: BN; + maxSupply?: BN; + printingMint: PublicKey; + oneTimePrintingAuthorizationMint: PublicKey; + constructor(args: { + key: MetadataKey; + supply: BN; + maxSupply?: BN; + printingMint: PublicKey; + oneTimePrintingAuthorizationMint: PublicKey; + }) { + this.key = MetadataKey.MasterEditionV1; + this.supply = args.supply; + this.maxSupply = args.maxSupply; + this.printingMint = args.printingMint; + this.oneTimePrintingAuthorizationMint = + args.oneTimePrintingAuthorizationMint; + }; +} + +export class MasterEditionV2 { + key: MetadataKey; + supply: BN; + maxSupply?: BN; + constructor(args: { + key: MetadataKey; + supply: BN; + maxSupply?: BN; + }) { + this.key = MetadataKey.MasterEditionV2; + this.supply = args.supply; + this.maxSupply = args.maxSupply; + }; +} + +export class EditionMarker { + key: MetadataKey; + ledger: number[]; + constructor(args: { + key: MetadataKey; + ledger: number[]; + }) { + this.key = MetadataKey.EditionMarker; + this.ledger = args.ledger; + }; +} + +export class Edition { + key: MetadataKey; + parent: PublicKey; + edition: BN; + constructor(args: { + key: MetadataKey; + parent: PublicKey; + edition: BN; + }) { + this.key = MetadataKey.EditionV1; + this.parent = args.parent; + this.edition = args.edition; + }; +} + +export class Data { + name: string; + symbol: string; + uri: string; + sellerFeeBasisPoints: number; + creators: Creator[] | null; + constructor(args: { + name: string; + symbol: string; + uri: string; + sellerFeeBasisPoints: number; + creators: Creator[] | null; + }) { + this.name = args.name; + this.symbol = args.symbol; + this.uri = args.uri; + this.sellerFeeBasisPoints = args.sellerFeeBasisPoints; + this.creators = args.creators; + }; +} + +export class Metadata { + key: MetadataKey; + updateAuthority: PublicKey; + mint: PublicKey; + data: Data; + primarySaleHappened: boolean; + isMutable: boolean; + masterEdition?: PublicKey; + edition?: PublicKey; + constructor(args: { + updateAuthority: PublicKey; + mint: PublicKey; + data: Data; + primarySaleHappened: boolean; + isMutable: boolean; + masterEdition?: PublicKey; + }) { + this.key = MetadataKey.MetadataV1; + this.updateAuthority = args.updateAuthority; + this.mint = args.mint; + this.data = args.data; + this.primarySaleHappened = args.primarySaleHappened; + this.isMutable = args.isMutable; + }; +} + +export const METADATA_SCHEMA = new Map([ + [ + MasterEditionV1, + { + kind: 'struct', + fields: [ + ['key', 'u8'], + ['supply', 'u64'], + ['maxSupply', { kind: 'option', type: 'u64' }], + ['printingMint', 'pubkey'], + ['oneTimePrintingAuthorizationMint', [32]], + ], + }, + ], + [ + MasterEditionV2, + { + kind: 'struct', + fields: [ + ['key', 'u8'], + ['supply', 'u64'], + ['maxSupply', { kind: 'option', type: 'u64' }], + ], + }, + ], + [ + Edition, + { + kind: 'struct', + fields: [ + ['key', 'u8'], + ['parent', [32]], + ['edition', 'u64'], + ], + }, + ], + [ + Data, + { + kind: 'struct', + fields: [ + ['name', 'string'], + ['symbol', 'string'], + ['uri', 'string'], + ['sellerFeeBasisPoints', 'u16'], + ['creators', { kind: 'option', type: [Creator] }], + ], + }, + ], + [ + Creator, + { + kind: 'struct', + fields: [ + ['address', [32]], + ['verified', 'u8'], + ['share', 'u8'], + ], + }, + ], + [ + Metadata, + { + kind: 'struct', + fields: [ + ['key', 'u8'], + ['updateAuthority', [32]], + ['mint', [32]], + ['data', Data], + ['primarySaleHappened', 'u8'], + ['isMutable', 'u8'], + ], + }, + ], + [ + EditionMarker, + { + kind: 'struct', + fields: [ + ['key', 'u8'], + ['ledger', [31]], + ], + }, + ], +]); \ No newline at end of file From fda50663d76c93295e05962926c4c17f400397d8 Mon Sep 17 00:00:00 2001 From: Bertrand Date: Wed, 15 Sep 2021 20:07:04 +0200 Subject: [PATCH 10/10] allow candy machine price update (#347) --- js/packages/cli/src/cli.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/js/packages/cli/src/cli.ts b/js/packages/cli/src/cli.ts index 96bb023..1e25cab 100755 --- a/js/packages/cli/src/cli.ts +++ b/js/packages/cli/src/cli.ts @@ -270,13 +270,15 @@ programCommand('create_candy_machine') log.info(`create_candy_machine finished. candy machine pubkey: ${candyMachine.toBase58()}`); }); -programCommand('set_start_date') +programCommand('update_candy_machine') .option('-d, --date ', 'timestamp - eg "04 Dec 1995 00:12:00 GMT"') + .option('-p, --price ', 'SOL price') .action(async (directory, cmd) => { - const { keypair, env, date, cacheName } = cmd.opts(); + const { keypair, env, date, price, cacheName } = cmd.opts(); const cacheContent = loadCache(cacheName, env); - const secondsSinceEpoch = (date ? Date.parse(date) : Date.now()) / 1000; + const secondsSinceEpoch = date ? Date.parse(date) / 1000 : null; + const lamports = price ? parsePrice(price) : null; const walletKeyPair = loadWalletKey(keypair); const anchorProgram = await loadAnchorProgram(walletKeyPair, env); @@ -286,8 +288,8 @@ programCommand('set_start_date') cacheContent.program.uuid, ); const tx = await anchorProgram.rpc.updateCandyMachine( - null, - new anchor.BN(secondsSinceEpoch), + lamports ? new anchor.BN(lamports) : null, + secondsSinceEpoch ? new anchor.BN(secondsSinceEpoch) : null, { accounts: { candyMachine, @@ -296,7 +298,9 @@ programCommand('set_start_date') }, ); - log.info('set_start_date Done', secondsSinceEpoch, tx); + if (date) log.info(` - updated startDate timestamp: ${secondsSinceEpoch} (${date})`) + if (lamports) log.info(` - updated price: ${lamports} lamports (${price} SOL)`) + log.info('updated_candy_machine Done', tx); }); programCommand('mint_one_token')