npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@metaplex-foundation/solita

v0.20.1

Published

Generates SDK API from solana contract IDL.

Downloads

45,567

Readme

solita Build Lint and Test Solita

Sol ana I DL t o A PI generator.

solita-logo

Table of Contents generated with DocToc

How does it Work?

Solita generates a low level TypeScript SDK for your Solana Rust programs from the IDL extracted by anchor or shank.

Shank + Solita Example (Recommended)

In order to use solita with shank do the following:

  • add the shank library to your Rust project via cargo add shank
  • annotate your Rust program as outlined here
  • add solita to the dev dependencies of your SDK package via yarn add -D @metaplex-foundation/solita
  • add a config similar to the below into .solitarc.js in your SDK package root
const path = require('path');
const programDir = path.join(__dirname, '..', 'program');
const idlDir = path.join(__dirname, 'idl');
const sdkDir = path.join(__dirname, 'src', 'generated');
const binaryInstallDir = path.join(__dirname, '.crates');

module.exports = {
  idlGenerator: 'shank',
  programName: 'mpl_token_vault',
  idlDir,
  sdkDir,
  binaryInstallDir,
  programDir,
};

Now running yarn solita from the same folder will take care of installing the matching shank binary and generating the IDL and SDK.

Run it each time you make a change to your program to generate the TypeScript SDK.

Since we're writing the shank binary to .crates/ you should add that folder to your .gitignore.

Full Example: Token Metadata Solita + Shank Setup

Anchor + Solita Example (Recommended)

In order to use solita with anchor do the following:

  • annotate your Rust program with anchor attributes
  • add solita to the dev dependencies of your SDK package via yarn add -D @metaplex-foundation/solita
  • add a config similar to the below into .solitarc.js in your SDK package root
const path = require('path');
const programDir = path.join(__dirname, '..', 'program');
const idlDir = path.join(__dirname, 'idl');
const sdkDir = path.join(__dirname, 'src', 'generated');
const binaryInstallDir = path.join(__dirname, '.crates');

module.exports = {
  idlGenerator: 'anchor',
  programName: 'auction_house',
  programId: 'hausS13jsjafwWwGqZTUQRmWyvyxn9EQpqMwV1PBBmk',
  idlDir,
  sdkDir,
  binaryInstallDir,
  programDir,
};

Now running yarn solita from the same folder will take care of installing the matching anchor binary and generating the IDL and SDK.

Run it each time you make a change to your program to generate the TypeScript SDK.

Since we're writing the anchor binary to .crates/ you should add that folder to your .gitignore.

NOTE: that for anchor generated IDL an optional anchorRemainingAccounts property is added to each set of instruction accounts. If your programs are not using those you can specifically turn that off by setting anchorRemainingAccounts: false.

Full Example: MPL Candy Machine Solita + Anchor Setup

Type Aliases

In order to have Solita resolve specific types to a Rust builtin type please provide a type alias map as in the below config. Solita then will treat those as if they were the aliased type.

module.exports = {
  idlGenerator: 'anchor',
  [ .. ]
  typeAliases: {
    UnixTimestamp: 'i64'
  }
};

Custom De/Serializers

For some accounts the generated de/serializers don't work. In those cases a custom de/serializer can be specified.

This is as simple as adding a module to your project which exports a either or both of the below functions:

export function deserialize(buf: Buffer, offset = 0): [<Account>, number] {
  [..]
}

export function serialize(instance: <Account>Args, byteSize?: number): [Buffer, number]
  [..]
}

Then provide them as serializers to Solita or via the solita config:

module.exports = {
  idlGenerator: 'shank',
  [ .. ]
  serializers: {
    Metadata: './src/custom/metadata-deserializer.ts',
  },
};

Hooking into IDL Creation

It is possible to modify the IDL generated by anchor or shank before it is passed to the solita code generator. Just provide an idlHook of the type (idl: Idl) => Idl via the solita config.

This hook takes the current idl as an input and returns the modified version. It is ok to modify the idl parameter in place if that is more convenient.

Please refer to the definition of the Idl type for more details.

Example:

module.exports = {
  idlGenerator: 'anchor',
  [ .. ]
  idlHook: (idl) => {
    return { ...idl, hola: 'mundo' }
  }
};

Advanced Shank + Solita Example

If you need more control you can also add a script. However you're on your own to ensure that the globally installed shank binary matches the version of its library you're using.

  • globally install shank via cargo install shank-cli
  • add a script similar to the below to your SDK package and
const path = require('path');
const { Solita } = require('@metaplex-foundation/solita');
const {
  rustbinMatch,
  confirmAutoMessageConsole,
} = require('@metaplex-foundation/rustbin')
const { spawn } = require('child_process');

const programDir = path.join(__dirname, '..', '..', 'program');
const cargoToml = path.join(programDir, 'Cargo.toml')
const generatedIdlDir = path.join(__dirname, '..', 'idl');
const generatedSDKDir = path.join(__dirname, '..', 'src', 'generated');
const rootDir = path.join(__dirname, '..', '.crates')

const PROGRAM_NAME = 'mpl_token_metadata';
const rustbinConfig = {
  rootDir,
  binaryName: 'shank',
  binaryCrateName: 'shank-cli',
  libName: 'shank',
  dryRun: false,
  cargoToml,
}

async function main() {
  const { fullPathToBinary: shankExecutable } = await rustbinMatch(
    rustbinConfig,
    confirmAutoMessageConsole
  )
  const shank = spawn(shankExecutable, ['idl', '--out-dir', generatedIdlDir, '--crate-root', programDir])
    .on('error', (err) => {
      console.error(err);
      if (err.code === 'ENOENT') {
        console.error(
          'Ensure that `shank` is installed and in your path, see:\n  https://github.com/metaplex-foundation/shank\n',
        );
      }
      process.exit(1);
    })
    .on('exit', () => {
      generateTypeScriptSDK();
    });

  shank.stdout.on('data', (buf) => console.log(buf.toString('utf8')));
  shank.stderr.on('data', (buf) => console.error(buf.toString('utf8')));
}

async function generateTypeScriptSDK() {
  console.error('Generating TypeScript SDK to %s', generatedSDKDir);
  const generatedIdlPath = path.join(generatedIdlDir, `${PROGRAM_NAME}.json`);

  const idl = require(generatedIdlPath);
  const gen = new Solita(idl, { formatCode: true });
  await gen.renderAndWriteTo(generatedSDKDir);

  console.error('Success!');

  process.exit(0);
}

main().catch((err) => {
  console.error(err)
  process.exit(1)
})

Advanced Anchor + Solita Example

If you need more control you can also add a script. However you're on your own to ensure that the globally installed anchor binary matches the version of its library you're using.

  • globally install anchor
  • add a script similar to the below to your SDK package
const path = require('path');
const {
  rustbinMatch,
  confirmAutoMessageConsole,
} = require('@metaplex-foundation/rustbin')
const { spawn } = require('child_process');
const { Solita } = require('@metaplex-foundation/solita');
const { writeFile } = require('fs/promises');

const PROGRAM_NAME = 'candy_machine';
const PROGRAM_ID = 'cndy3Z4yapfJBmL3ShUp5exZKqR3z33thTzeNMm2gRZ';

const programDir = path.join(__dirname, '..', '..', 'program');
const cargoToml = path.join(programDir, 'Cargo.toml')
const generatedIdlDir = path.join(__dirname, '..', 'idl');
const generatedSDKDir = path.join(__dirname, '..', 'src', 'generated');
const rootDir = path.join(__dirname, '..', '.crates')

async function main() {
  const { fullPathToBinary: anchorExecutable } = await rustbinMatch(
    rustbinConfig,
    confirmAutoMessageConsole
  )
  const anchor = spawn(anchorExecutable, ['build', '--idl', generatedIdlDir], { cwd: programDir })
    .on('error', (err) => {
      console.error(err);
      // @ts-ignore this err does have a code
      if (err.code === 'ENOENT') {
        console.error(
          'Ensure that `anchor` is installed and in your path, see:\n  https://project-serum.github.io/anchor/getting-started/installation.html#install-anchor\n',
        );
      }
      process.exit(1);
    })
    .on('exit', () => {
      console.log('IDL written to: %s', path.join(generatedIdlDir, `${PROGRAM_NAME}.json`));
      generateTypeScriptSDK();
    });

  anchor.stdout.on('data', (buf) => console.log(buf.toString('utf8')));
  anchor.stderr.on('data', (buf) => console.error(buf.toString('utf8')));
}

async function generateTypeScriptSDK() {
  console.error('Generating TypeScript SDK to %s', generatedSDKDir);
  const generatedIdlPath = path.join(generatedIdlDir, `${PROGRAM_NAME}.json`);

  const idl = require(generatedIdlPath);
  if (idl.metadata?.address == null) {
    idl.metadata = { ...idl.metadata, address: PROGRAM_ID };
    await writeFile(generatedIdlPath, JSON.stringify(idl, null, 2));
  }
  const gen = new Solita(idl, { formatCode: true });
  await gen.renderAndWriteTo(generatedSDKDir);

  console.error('Success!');

  process.exit(0);
}

main().catch((err) => {
  console.error(err)
  process.exit(1)
})

Solita in the Wild

Find more solita, shank and anchor examples inside the metaplex-program-library.

LICENSE

Apache-2.0