Features
Registering Programs
In order to create clients that interact with Solana programs, it is important to know which programs are available within your cluster and at which address. Umi offers a ProgramRepositoryInterface
that acts as one big registry of programs for you client.
This also allows us to:
- Register programs from other libraries.
- Register our own programs and override existing ones.
- Fetch programs by name or public key in our current cluster or a specific one.
- Resolve program errors by name or code.
Defining Programs
Umi provides a Program
type that represents a Solana program. It contains the program's name, public key, and some functions that can be used to resolve its errors and which cluster it has been deployed to.
export type Program = {
name: string;
publicKey: PublicKey;
getErrorFromCode: (code: number, cause?: Error) => ProgramError | null;
getErrorFromName: (name: string, cause?: Error) => ProgramError | null;
isOnCluster: (cluster: Cluster) => boolean;
};
You can learn more about the attributes of the Program
type via its API reference but note that the name
attribute should be unique and, by convention, should be use the camelCase format. To avoid conflict with other organizations, it is recommended to prefix the program name with a namespace that is unique to your organization. For instance, Metaplex programs are prefixed with mpl
like so: mplTokenMetadata
or mplCandyMachine
.
Adding Programs
To register a new program to the program repository, you may use the add
method of the ProgramRepositoryInterface
like so.
umi.programs.add(myProgram);
If this program already exists in the repository — i.e. it has the same name or public key for at least one conflicting cluster — it will be overridden by the newly added program. To change this behavior, you may set the second argument override
to false
. In the example below, this program will only the retrieved if no other registered program matches the user's query.
umi.programs.add(myProgram, false);
Fetching Programs
Once a program is registered, you may fetch it by its name or public key via the get
method. This will return the program if it exists in the repository. Otherwise, it will throw an error.
// Fetch a program by its name.
const myProgram = umi.programs.get('myProgram');
// Fetch a program by its public key.
const myProgram = umi.programs.get(publicKey('...'));
By default, the get
method will only return programs that are deployed to the current cluster — such that the isOnCluster
method returns true
for the current cluster. It is only possible to specify a different cluster via the second argument that accepts a ClusterFilter
.
A ClusterFilter
can either be an explicit Cluster
, "current"
to select the current cluster, or "all"
to select programs that are deployed to any cluster.
// Fetch a program on the current cluster.
umi.programs.get('myProgram');
umi.programs.get('myProgram', 'current');
// Fetch a program on a specific cluster.
umi.programs.get('myProgram', 'mainnet-beta');
umi.programs.get('myProgram', 'devnet');
// Fetch a program on any cluster.
umi.programs.get('myProgram', 'all');
It is also worth noting that the get
method is generic and can return a superset of the Program
type. For instance, say you have a CandyGuardProgram
type that extends the Program
type in order to store the availableGuards
on that program. Then, if you know that the program you are fetching should be of that type, you may tell the get
method by setting its type parameter to CandyGuardProgram
.
umi.programs.get<CandyGuardProgram>('mplCandyGuard');
Additionally, the ProgramRepositoryInterface
provides a has
method that can be used to check if a program exists in the repository and a all
method to retrieve all programs in the repository. Both of these methods accept the same ClusterFilter
argument as the get
method.
// Check if a program exists in the repository.
umi.programs.has('myProgram');
umi.programs.has(publicKey('...'));
umi.programs.has('myProgram', 'mainnet-beta');
umi.programs.has('myProgram', 'all');
// Retrieve all programs in the repository.
umi.programs.all();
umi.programs.all('mainnet-beta');
umi.programs.all('all');
Finally, since fetching the public key of a program is a common operation, the ProgramRepositoryInterface
provides a getPublicKey
method that can be used to fetch a program's public key directly. A fallback
public key can be provided to avoid throwing an error if the program does not exist in the repository and return the given public key instead.
// Get the public key of a program.
umi.programs.getPublicKey('myProgram');
// Get the public key of a program with a fallback.
const fallback = publicKey('...');
umi.programs.getPublicKey('myProgram', fallback);
// Get the public key of a program on a specific cluster.
umi.programs.getPublicKey('myProgram', fallback, 'mainnet-beta');
Resolving program errors
The ProgramRepositoryInterface
provides a resolveError
method that can be used to resolve a custom program error from a transaction error. This method accepts any Error
with a logs
attribute and the Transaction
instance that originated this error. It then returns an instance of ProgramError
if a custom program error was identified from the error logs. Otherwise, it returns null
.
umi.programs.resolveError(error, transaction);