Skip to content

Commit

Permalink
pg: devnet support (#361)
Browse files Browse the repository at this point in the history
Easier to test vault creation and acls etc using devnet.
  • Loading branch information
yurushao authored Jan 8, 2025
1 parent c6f2224 commit 4d71452
Show file tree
Hide file tree
Showing 15 changed files with 96 additions and 97 deletions.
48 changes: 34 additions & 14 deletions anchor/src/client/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,14 @@ import {
} from "@solana/spl-token";
import { WSOL, USDC } from "../constants";

import { GlamIDL, GlamProgram, getGlamProgramId } from "../glamExports";
import { ClusterOrCustom, GlamClientConfig } from "../clientConfig";
import {
Glam,
GlamIDL,
GlamIDLJson,
GlamProgram,
getGlamProgramId,
} from "../glamExports";
import { ClusterNetwork, GlamClientConfig } from "../clientConfig";
import {
FundAccount,
FundMetadataAccount,
Expand Down Expand Up @@ -72,17 +78,15 @@ export type TokenAccount = {
};

export class BaseClient {
cluster: ClusterOrCustom;
cluster: ClusterNetwork;
provider: anchor.Provider;
program: GlamProgram;
programId: PublicKey;
jupiterApi: string;
blockhashWithCache: BlockhashWithCache;

public constructor(config?: GlamClientConfig) {
if (config?.provider) {
this.provider = config?.provider;
this.program = new Program(GlamIDL, this.provider) as GlamProgram;
} else {
const defaultProvider = anchor.AnchorProvider.env();
const url = defaultProvider.connection.rpcEndpoint;
Expand All @@ -100,17 +104,29 @@ export class BaseClient {
},
);
anchor.setProvider(this.provider);
this.program = anchor.workspace.Glam as GlamProgram;
}

// autodetect mainnet
const defaultCluster = this.provider.connection.rpcEndpoint.includes(
"mainnet",
)
? "mainnet-beta"
: "devnet";
? ClusterNetwork.Mainnet
: ClusterNetwork.Devnet;
this.cluster = config?.cluster || defaultCluster;
this.programId = getGlamProgramId(this.cluster);

if (this.cluster === ClusterNetwork.Mainnet) {
this.program = new Program(GlamIDL, this.provider) as GlamProgram;
} else {
const GlamIDLDevnet = { ...GlamIDLJson };
GlamIDLDevnet.address = getGlamProgramId(
ClusterNetwork.Devnet,
).toBase58();
this.program = new Program(
GlamIDLDevnet as Glam,
this.provider,
) as GlamProgram;
}

this.jupiterApi = config?.jupiterApi || JUPITER_API_DEFAULT;
this.blockhashWithCache = new BlockhashWithCache(
this.provider,
Expand All @@ -119,7 +135,7 @@ export class BaseClient {
}

isMainnet(): boolean {
return this.cluster === "mainnet-beta";
return this.cluster === ClusterNetwork.Mainnet;
}

isPhantom(): boolean {
Expand Down Expand Up @@ -350,15 +366,15 @@ export class BaseClient {
manager.toBuffer(),
Uint8Array.from(createdKey),
],
this.programId,
this.program.programId,
);
return pda;
}

getVaultPda(fundPDA: PublicKey): PublicKey {
const [pda, _bump] = PublicKey.findProgramAddressSync(
[Buffer.from("treasury"), fundPDA.toBuffer()],
this.programId,
this.program.programId,
);
return pda;
}
Expand Down Expand Up @@ -449,7 +465,11 @@ export class BaseClient {
}

getOpenfundsPDA(fundPDA: PublicKey): PublicKey {
return FundModel.openfundsPda(fundPDA);
const [pda, _] = PublicKey.findProgramAddressSync(
[Buffer.from("openfunds"), fundPDA.toBuffer()],
this.program.programId,
);
return pda;
}

getShareClassPDA(fundPDA: PublicKey, shareId: number = 0): PublicKey {
Expand Down Expand Up @@ -560,7 +580,7 @@ export class BaseClient {
0x31, 0x68, 0xa8, 0xd6, 0x86, 0xb4, 0xad, 0x9a,
]);
const accounts = await this.provider.connection.getParsedProgramAccounts(
this.programId,
this.program.programId,
{
filters: [{ memcmp: { offset: 0, bytes: bs58.encode(bytes) } }],
},
Expand Down
2 changes: 1 addition & 1 deletion anchor/src/client/marinade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class MarinadeClient {
): [PublicKey, number] {
return PublicKey.findProgramAddressSync(
[Buffer.from("ticket"), Buffer.from(ticketId), fundPDA.toBuffer()],
this.base.programId,
this.base.program.programId,
);
}

Expand Down
2 changes: 1 addition & 1 deletion anchor/src/client/staking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export class StakingClient {
Buffer.from(accountId),
fundPDA.toBuffer(),
],
this.base.programId,
this.base.program.programId,
);
}

Expand Down
12 changes: 7 additions & 5 deletions anchor/src/clientConfig.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Provider, Wallet } from "@coral-xyz/anchor";
import { Cluster } from "@solana/web3.js";

export type ClusterOrCustom = Cluster | "custom";

export enum ClusterNetwork {
Mainnet = "mainnet-beta",
Testnet = "testnet",
Devnet = "devnet",
Custom = "custom",
}
export type GlamClientConfig = {
mainnet?: boolean;
provider?: Provider;
wallet?: Wallet;
cluster?: ClusterOrCustom;
cluster?: ClusterNetwork;
jupiterApi?: string;
};
7 changes: 3 additions & 4 deletions anchor/src/glamExports.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// Here we export some useful types and functions for interacting with the Anchor program.
import { Program } from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";

import type { ClusterNetwork } from "./clientConfig";
import type { Glam } from "../target/types/glam";
import GlamIDLJson from "../target/idl/glam.json";

import type { ClusterOrCustom } from "./clientConfig";

const GlamIDL = GlamIDLJson as Glam;
export { Glam, GlamIDL, GlamIDLJson };
export type GlamProgram = Program<Glam>;
Expand All @@ -17,7 +16,7 @@ export const GLAM_PROGRAM_ID_MAINNET = new PublicKey(
"GLAMpLuXu78TA4ao3DPZvT1zQ7woxoQ8ahdYbhnqY9mP",
);

export function getGlamProgramId(cluster: ClusterOrCustom) {
export function getGlamProgramId(cluster: ClusterNetwork) {
switch (cluster) {
case "mainnet-beta":
return GLAM_PROGRAM_ID_MAINNET;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { clusterApiUrl, Connection } from "@solana/web3.js";
import { atom, useAtomValue, useSetAtom } from "jotai";
import { atomWithStorage } from "jotai/utils";
import { createContext, ReactNode, useContext } from "react";
import toast from "react-hot-toast";
import { ClusterNetwork } from "../clientConfig";

interface Cluster {
name: string;
Expand All @@ -13,13 +13,6 @@ interface Cluster {
active?: boolean;
}

export enum ClusterNetwork {
Mainnet = "mainnet-beta",
Testnet = "testnet",
Devnet = "devnet",
Custom = "custom",
}

const defaultClusters: Cluster[] = [
{
name: "mainnet-beta",
Expand All @@ -29,28 +22,33 @@ const defaultClusters: Cluster[] = [
},
{
name: "devnet",
endpoint: clusterApiUrl("devnet"),
endpoint:
process.env.NEXT_PUBLIC_SOLANA_RPC?.replace("mainnet", "devnet") ||
clusterApiUrl("devnet"),
network: ClusterNetwork.Devnet,
},
{
// {
// name: "testnet",
// endpoint: clusterApiUrl("testnet"),
// network: ClusterNetwork.Testnet,
// },
];

if (process.env.NODE_ENV === "development") {
defaultClusters.push({
name: "localnet",
endpoint: "http://localhost:8899",
network: ClusterNetwork.Custom,
},
{
name: "testnet",
endpoint: clusterApiUrl("testnet"),
network: ClusterNetwork.Testnet,
},
];
});
}

const clusterAtom = atomWithStorage<Cluster>(
"solana-cluster",
defaultClusters[0]
defaultClusters[0],
);
const clustersAtom = atomWithStorage<Cluster[]>(
"solana-clusters",
defaultClusters
defaultClusters,
);

const activeClustersAtom = atom<Cluster[]>((get) => {
Expand Down Expand Up @@ -78,7 +76,7 @@ interface ClusterProviderContext {
}

const Context = createContext<ClusterProviderContext>(
{} as ClusterProviderContext
{} as ClusterProviderContext,
);

export function ClusterProvider({ children }: { children: ReactNode }) {
Expand All @@ -95,7 +93,7 @@ export function ClusterProvider({ children }: { children: ReactNode }) {
new Connection(cluster.endpoint);
setClusters([...clusters, cluster]);
} catch (err) {
toast.error(`${err}`);
throw err;
}
},
deleteCluster: (cluster: Cluster) => {
Expand Down
13 changes: 8 additions & 5 deletions anchor/src/react/glam.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from "@solana/spl-token";
import { DriftMarketConfigs, GlamDriftUser } from "../client/drift";
import { TokenAccount } from "../client/base";
import { useCluster } from "./cluster-provider";

export interface JupTokenListItem {
address: string;
Expand Down Expand Up @@ -134,25 +135,25 @@ const fetchBalances = async (glamClient: GlamClient, owner: PublicKey) => {

export function GlamProvider({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
}: Readonly<{ children: React.ReactNode }>) {
const setActiveFund = useSetAtom(fundAtom);
const setFundsList = useSetAtom(fundsListAtom);

const [treasury, setTreasury] = useState({} as Treasury);
const [userWallet, setUserWallet] = useState({} as UserWallet);
const wallet = useWallet();
const { connection } = useConnection();
const { cluster } = useCluster();

const glamClient = useMemo(
() =>
new GlamClient({
provider: new AnchorProvider(connection, wallet as AnchorWallet, {
commitment: "confirmed",
}),
cluster: cluster.network,
}),
[connection, wallet],
[connection, wallet, cluster],
);
const [allFunds, setAllFunds] = useState([] as FundModel[]);
const [jupTokenList, setJupTokenList] = useState([] as JupTokenListItem[]);
Expand Down Expand Up @@ -238,13 +239,14 @@ export function GlamProvider({
}

refreshTreasury();
}, [allFundsData, activeFund, wallet]);
}, [allFundsData, activeFund, wallet, cluster]);

//
// Fetch token prices https://station.jup.ag/docs/apis/price-api-v2
//
const { data: jupTokenPricesData } = useQuery({
queryKey: ["/jup-token-prices", treasury?.pubkey],
enabled: cluster.network === "mainnet-beta",
refetchInterval: 10_000,
queryFn: () => {
const tokenMints = new Set([] as string[]);
Expand Down Expand Up @@ -332,6 +334,7 @@ export function GlamProvider({
//
const { data: marketConfigs } = useQuery({
queryKey: ["drift-market-configs"],
enabled: cluster.network === "mainnet-beta",
queryFn: async () => {
const response = await fetch(
"https://api.glam.systems/v0/drift/market_configs/",
Expand Down
1 change: 1 addition & 0 deletions anchor/src/react/index.tsx
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "../index";
export * from "./glam";
export * from "./cluster-provider";
2 changes: 1 addition & 1 deletion cli/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ try {
const config = fs.readFileSync(configPath, "utf8");
const { keypair_path, helius_api_key, priority_fee_level, fund } =
JSON.parse(config);
process.env.ANCHOR_PROVIDER_URL = `https://mainnet.helius-rpc.com/?api-key=${helius_api_key}`;
process.env.ANCHOR_PROVIDER_URL = `https://devnet.helius-rpc.com/?api-key=${helius_api_key}`;
process.env.ANCHOR_WALLET = keypair_path;
if (fund) {
fundPDA = new PublicKey(fund);
Expand Down
23 changes: 2 additions & 21 deletions cli/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,10 @@
import fs from "fs";
import * as anchor from "@coral-xyz/anchor";
import { Connection } from "@solana/web3.js";
import { GlamClient } from "@glam/anchor";
import { ClusterNetwork, GlamClient } from "@glam/anchor";

export const getGlamClient = () => {
const defaultProvider = anchor.AnchorProvider.env();
const url = defaultProvider.connection.rpcEndpoint;
const connection = new Connection(url, {
commitment: "confirmed",
});
const provider = new anchor.AnchorProvider(
connection,
defaultProvider.wallet,
{
...defaultProvider.opts,
commitment: "confirmed",
preflightCommitment: "confirmed",
}
);
anchor.setProvider(provider);

return new GlamClient({
provider,
cluster: "mainnet-beta",
});
return new GlamClient();
};

export const setFundToConfig = (fund, path) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import { Label } from "@/components/ui/label";
import React from "react";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { useCluster } from "@/components/solana-cluster-provider";
import { toast } from "@/components/ui/use-toast";
import { useCluster } from "@glam/anchor/react";

export default function Cluster() {
const { cluster, clusters, setCluster } = useCluster();
Expand Down
Loading

0 comments on commit 4d71452

Please sign in to comment.