Skip to content

Commit

Permalink
Improve init store (metaplex-foundation#213)
Browse files Browse the repository at this point in the history
* chore: missed env CLIENT_ID used NEXT_PUBLIC prefix now

* chore: return missed #root

* chore: organize styles

* feat: storeProvider

* feat: STORE_ADDRESS in ENV

* feat: improve store initialization
  • Loading branch information
exromany authored Aug 19, 2021
1 parent f934ced commit a4718e8
Show file tree
Hide file tree
Showing 29 changed files with 692 additions and 504 deletions.
9 changes: 1 addition & 8 deletions js/packages/common/src/contexts/connection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
import React, { useContext, useEffect, useMemo, useState } from 'react';
import { notify } from '../utils/notifications';
import { ExplorerLink } from '../components/ExplorerLink';
import { setProgramIds, setStoreID } from '../utils/programIds';
import {
TokenInfo,
TokenListProvider,
Expand Down Expand Up @@ -79,10 +78,7 @@ const ConnectionContext = React.createContext<ConnectionConfig>({
tokenMap: new Map<string, TokenInfo>(),
});

export function ConnectionProvider({
storeId = undefined as any,
children = undefined as any,
}) {
export function ConnectionProvider({ children = undefined as any }) {
const [endpoint, setEndpoint] = useLocalStorageState(
'connectionEndpoint',
ENDPOINTS[0].endpoint,
Expand Down Expand Up @@ -119,9 +115,6 @@ export function ConnectionProvider({
});
}, [env]);

setStoreID(storeId);
setProgramIds(env);

// The websocket library solana/web3.js uses closes its websocket connection when the subscription list
// is empty after opening its first time, preventing subsequent subscriptions from receiving responses.
// This is a hack to prevent the list from every getting empty
Expand Down
1 change: 1 addition & 0 deletions js/packages/common/src/contexts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { ParsedAccount, ParsedAccountBase } from './accounts';
export * from './accounts';
export * from './wallet';
export * from './connection';
export * from './store';
73 changes: 73 additions & 0 deletions js/packages/common/src/contexts/store.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, {
createContext,
FC,
useState,
useContext,
useEffect,
useMemo,
} from 'react';
import { getStoreID, setProgramIds, StringPublicKey } from '../utils';
import { useQuerySearch } from '../hooks';

interface StoreConfig {
// Store Address
storeAddress?: StringPublicKey;
// Store was configured via ENV or query params
isConfigured: boolean;
// Initial calculating of store address completed (successfully or not)
isReady: boolean;
// recalculate store address for specified owner address
setStoreForOwner: (ownerAddress?: string) => Promise<string | undefined>;
}

export const StoreContext = createContext<StoreConfig>(null!);

export const StoreProvider: FC<{
ownerAddress?: string;
storeAddress?: string;
}> = ({ children, ownerAddress, storeAddress }) => {
const searchParams = useQuerySearch();
const ownerAddressFromQuery = searchParams.get('store');

const initOwnerAddress = ownerAddressFromQuery || ownerAddress;
const initStoreAddress = !ownerAddressFromQuery ? storeAddress : undefined;
const isConfigured = Boolean(initStoreAddress || initOwnerAddress);

const [store, setStore] = useState<
Pick<StoreConfig, 'storeAddress' | 'isReady'>
>({
storeAddress: initStoreAddress,
isReady: Boolean(!initOwnerAddress || initStoreAddress),
});

const setStoreForOwner = useMemo(
() => async (ownerAddress?: string) => {
const storeAddress = await getStoreID(ownerAddress);
setProgramIds(storeAddress); // fallback
setStore({ storeAddress, isReady: true });
console.log(`CUSTOM STORE: ${storeAddress}`);
return storeAddress;
},
[],
);

useEffect(() => {
console.log(`STORE_OWNER_ADDRESS: ${initOwnerAddress}`);
if (initOwnerAddress && !initStoreAddress) {
setStoreForOwner(initOwnerAddress);
} else {
setProgramIds(initStoreAddress); // fallback
console.log(`CUSTOM STORE FROM ENV: ${initStoreAddress}`);
}
}, [initOwnerAddress]);

return (
<StoreContext.Provider value={{ ...store, setStoreForOwner, isConfigured }}>
{children}
</StoreContext.Provider>
);
};

export const useStore = () => {
return useContext(StoreContext);
};
1 change: 1 addition & 0 deletions js/packages/common/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './useUserAccounts';
export * from './useAccountByMint';
export * from './useTokenName';
export * from './useThatState';
export * from './useQuerySearch';
5 changes: 5 additions & 0 deletions js/packages/common/src/hooks/useQuerySearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { useLocation } from 'react-router-dom';

export function useQuerySearch() {
return new URLSearchParams(useLocation().search);
}
64 changes: 7 additions & 57 deletions js/packages/common/src/utils/programIds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,76 +14,26 @@ import {
toPublicKey,
} from './ids';

export const ENABLE_FEES_INPUT = false;

// legacy pools are used to show users contributions in those pools to allow for withdrawals of funds
export const PROGRAM_IDS = [
{
name: 'mainnet-beta',
},
{
name: 'testnet',
},

{
name: 'devnet',
},
{
name: 'localnet',
},
];

let STORE_OWNER_ADDRESS: PublicKey | undefined;

export const setStoreID = (storeId: any) => {
STORE_OWNER_ADDRESS = storeId
? new PublicKey(`${storeId}`)
: // DEFAULT STORE FRONT OWNER FOR METAPLEX
undefined;
};

const getStoreID = async () => {
if (!STORE_OWNER_ADDRESS) {
export const getStoreID = async (storeOwnerAddress?: string) => {
if (!storeOwnerAddress) {
return undefined;
}

let urlStoreId: PublicKey | null = null;
try {
const urlParams = new URLSearchParams(window.location.search);
const text = urlParams.get('store');
if (text) {
urlStoreId = new PublicKey(text);
}
} catch {
// ignore
}

const storeOwnerAddress = urlStoreId || STORE_OWNER_ADDRESS;
console.log(`STORE_OWNER_ADDRESS: ${storeOwnerAddress?.toBase58()}`);
const programs = await findProgramAddress(
[
Buffer.from('metaplex'),
toPublicKey(METAPLEX_ID).toBuffer(),
storeOwnerAddress.toBuffer(),
toPublicKey(storeOwnerAddress).toBuffer(),
],
toPublicKey(METAPLEX_ID),
);
const CUSTOM = programs[0];
console.log(`CUSTOM STORE: ${CUSTOM}`);
const storeAddress = programs[0];

return CUSTOM;
return storeAddress;
};

export const setProgramIds = async (envName: string) => {
let instance = PROGRAM_IDS.find(env => envName.indexOf(env.name) >= 0);
if (!instance) {
return;
}

if (!STORE) {
const potential_store = await getStoreID();
STORE = potential_store ? toPublicKey(potential_store) : undefined;
}
export const setProgramIds = async (store?: string) => {
STORE = store ? toPublicKey(store) : undefined;
};

let STORE: PublicKey | undefined;
Expand Down
2 changes: 1 addition & 1 deletion js/packages/common/src/wallet-adapters/torus/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class TorusWalletAdapter extends EventEmitter implements WalletAdapter {

connect = async () => {
this._provider = new OpenLogin({
clientId: process.env.REACT_APP_CLIENT_ID || 'BKBTX-SmaEFGddZQrwqd65YFoImRQLca_Tj2IdmKyD2UbDpzrtN2WQ-NYLuej6gP0DfF3jSpEkI13wPt1uPedm0',
clientId: process.env.NEXT_PUBLIC_CLIENT_ID || 'BKBTX-SmaEFGddZQrwqd65YFoImRQLca_Tj2IdmKyD2UbDpzrtN2WQ-NYLuej6gP0DfF3jSpEkI13wPt1uPedm0',
network: "mainnet", // mainnet, testnet, development
uxMode: 'popup'
});
Expand Down
1 change: 1 addition & 0 deletions js/packages/web/.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
REACT_APP_STORE_OWNER_ADDRESS_ADDRESS=
REACT_APP_STORE_ADDRESS=
REACT_APP_BIG_STORE=FALSE
8 changes: 6 additions & 2 deletions js/packages/web/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ module.exports = withPlugins(plugins, {
eslint: {
ignoreDuringBuilds: true,
},
env:{
NEXT_PUBLIC_STORE_OWNER_ADDRESS_ADDRESS: process.env.REACT_APP_STORE_OWNER_ADDRESS_ADDRESS,
env: {
NEXT_PUBLIC_STORE_OWNER_ADDRESS:
process.env.STORE_OWNER_ADDRESS ||
process.env.REACT_APP_STORE_OWNER_ADDRESS_ADDRESS,
NEXT_PUBLIC_STORE_ADDRESS: process.env.STORE_ADDRESS,
NEXT_PUBLIC_BIG_STORE: process.env.REACT_APP_BIG_STORE,
NEXT_PUBLIC_CLIENT_ID: process.env.REACT_APP_CLIENT_ID,
},
async rewrites() {
return [
Expand Down
11 changes: 6 additions & 5 deletions js/packages/web/src/actions/saveAdmin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import {
import { WhitelistedCreator } from '../models/metaplex';
import { setStore } from '../models/metaplex/setStore';
import { setWhitelistedCreator } from '../models/metaplex/setWhitelistedCreator';
import { WalletAdapter } from '@solana/wallet-base';

// 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
export async function saveAdmin(
connection: Connection,
wallet: any,
wallet: WalletAdapter,
isPublic: boolean,
whitelistedCreators: WhitelistedCreator[],
) {
Expand All @@ -25,8 +26,8 @@ export async function saveAdmin(

await setStore(
isPublic,
wallet.publicKey.toBase58(),
wallet.publicKey.toBase58(),
wallet.publicKey!.toBase58(),
wallet.publicKey!.toBase58(),
storeInstructions,
);
signers.push(storeSigners);
Expand All @@ -40,8 +41,8 @@ export async function saveAdmin(
await setWhitelistedCreator(
wc.address,
wc.activated,
wallet.publicKey.toBase58(),
wallet.publicKey.toBase58(),
wallet.publicKey!.toBase58(),
wallet.publicKey!.toBase58(),
wcInstructions,
);
signers.push(wcSigners);
Expand Down
2 changes: 0 additions & 2 deletions js/packages/web/src/components/AppBar/index.less
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@import '../../_colors.less';

.App-Bar {
padding: 0px;
justify-content: space-between !important;
Expand Down
36 changes: 19 additions & 17 deletions js/packages/web/src/components/AppBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,30 @@ const UserActions = () => {

const canCreate = useMemo(() => {
return (
store &&
store.info &&
(store.info.public ||
whitelistedCreatorsByCreator[pubkey]?.info?.activated)
store?.info?.public ||
whitelistedCreatorsByCreator[pubkey]?.info?.activated
);
}, [pubkey, whitelistedCreatorsByCreator, store]);

return (
<>
{/* <Link to={`#`}>
<Button className="app-btn">Bids</Button>
</Link> */}
{canCreate ? (
<Link to={`/art/create`}>
<Button className="app-btn">Create</Button>
</Link>
) : null}
<Link to={`/auction/create/0`}>
<Button className="connector" type="primary">
Sell
</Button>
</Link>
{store && (
<>
{/* <Link to={`#`}>
<Button className="app-btn">Bids</Button>
</Link> */}
{canCreate ? (
<Link to={`/art/create`}>
<Button className="app-btn">Create</Button>
</Link>
) : null}
<Link to={`/auction/create/0`}>
<Button className="connector" type="primary">
Sell
</Button>
</Link>
</>
)}
</>
);
};
Expand Down
45 changes: 45 additions & 0 deletions js/packages/web/src/components/SetupVariables/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { CopyOutlined } from '@ant-design/icons';
import { Button, Card } from 'antd';
import { FC } from 'react';
import { useCallback, useRef } from 'react';

interface Variables {
storeAddress?: string;
storeOwnerAddress?: string;
}

export const SetupVariables: FC<Variables> = ({
storeAddress,
storeOwnerAddress,
}) => {
const ref = useRef<HTMLDivElement>(null);

const copySettings = useCallback(() => {
const text = ref.current?.innerText;
if (text) {
navigator.clipboard.writeText(text);
}
}, []);

if (!storeAddress && !storeOwnerAddress) {
return null;
}

return (
<Card
title="Store configuration"
extra={
<Button
type="dashed"
onClick={copySettings}
icon={<CopyOutlined />}
></Button>
}
>
<div ref={ref}>
{storeOwnerAddress && <p>STORE_OWNER_ADDRESS={storeOwnerAddress}</p>}
{storeAddress && <p>STORE_ADDRESS={storeAddress}</p>}
</div>
</Card>
);
};
8 changes: 2 additions & 6 deletions js/packages/web/src/contexts/meta/loadAccounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { processMetaplexAccounts } from './processMetaplexAccounts';
import { processMetaData } from './processMetaData';
import { processVaultData } from './processVaultData';
import {
findProgramAddress,
getEdition,
getMultipleAccounts,
MAX_CREATOR_LEN,
Expand All @@ -25,11 +24,8 @@ import {
Metadata,
METADATA_PREFIX,
ParsedAccount,
} from '../../../../common/dist/lib';
import {
MAX_WHITELISTED_CREATOR_SIZE,
MetaplexKey,
} from '../../models/metaplex';
} from '@oyster/common';
import { MAX_WHITELISTED_CREATOR_SIZE } from '../../models/metaplex';

async function getProgramAccounts(
connection: Connection,
Expand Down
Loading

0 comments on commit a4718e8

Please sign in to comment.