metaplex-foundation / solita

Genrates an SDK API from solana contract IDL.
Apache License 2.0
140 stars 32 forks source link

Autogenerate InstructionWithSigners #34

Closed lorisleiva closed 2 years ago

lorisleiva commented 2 years ago

This is just a reminder that we should consider auto-generating TransactionBuilders and GpaBuilders for MPL programs since we have all the information we need on the IDL.

The js-next SDK already contains lots of examples of such classes inside the programs folder. However, the SDK architecture is still evolving to account for more use-cases so let's give it a bit longer before we make these interfaces more difficult to change.


See https://github.com/metaplex-foundation/js-next/issues/3

lorisleiva commented 2 years ago

Note: If/when we auto-generate transaction builders, we'd need to move the program recognition away from instructions towards transactionBuilders. By that I mean if the variable is called tokenProgram we automatically provide the token address but I think the instruction should still require it (same as system and spl-token instructions). However, the transaction builders can be clever and expose an optional parameter that defaults to the recognised program address.

EDIT: That has been solved by creating a new type InstructionWithSigners that can be fed directly into a TransactionBuilder. Therefore Solita can take care of generating InstructionWithSigners object without having to know/care about how it's going to be used by the SDKs.

lorisleiva commented 2 years ago

I'm going to start posting some examples of autogenerated pieces of code below. But first, here are the types that these will be relying on:

import type {
  PublicKey,
  Transaction,
  TransactionInstruction,
} from '@solana/web3.js';

type Signer = KeypairSigner | IdentitySigner;

type KeypairSigner = {
  publicKey: PublicKey;
  secretKey: Uint8Array;
};

type IdentitySigner = {
  publicKey: PublicKey;
  signMessage(message: Uint8Array): Promise<Uint8Array>;
  signTransaction(transaction: Transaction): Promise<Transaction>;
  signAllTransactions(transactions: Transaction[]): Promise<Transaction[]>;
};

type InstructionWithSigners = {
  instruction: TransactionInstruction;
  signers: Signer[];
  key?: string;
}
lorisleiva commented 2 years ago

Note that we also need to tackle this issue: https://github.com/metaplex-foundation/solita/issues/44 first because it will allow us to also make the program id overridable.

lorisleiva commented 2 years ago

And here is a concrete example with the CreateMetadataAccountV2.

I think these should be generated directly inside the current instruction file (e.g. CreateMetadataAccountV2) at the end of the file. That way, there's no need to import the createCreateMetadataAccountV2Instruction function since it's already here.

export type CreateCreateMetadataAccountV2InstructionWithSignersParams = {
  // Program ID.
  // Defaults to the address on the IDL.
  programId?: PublicKey;

  // Accounts.
  // Can be PublicKey or Signer (from type above).
  accounts: { 
      metadata: PublicKey;
      mint: PublicKey;
      mintAuthority: Signer;
      payer: Signer;
      updateAuthority: PublicKey;
  }

  // Arguments.
  // As they are generated on the method that creates the instruction.
  args: CreateMetadataAccountV2InstructionArgs;

  // Instruction Key.
  // Defaults to the name of the instruction in camelCase.
  instructionKey?: string;
}

export function createCreateMetadataAccountV2InstructionWithSigners(
  params: CreateCreateMetadataAccountV2InstructionWithSignersParams
): InstructionWithSigners {
  return {
    instruction: createCreateMetadataAccountV2Instruction(
      {
        metadata: params.metadata,
        mint: params.mint,
        mintAuthority: params.mintAuthority.publicKey,
        payer: params.payer.publicKey,
        updateAuthority: params.updateAuthority,
      },
      params.args,
      params.programId // <-- Issue #44 should allow this.
    ),
    signers: [params.mintAuthority, params.payer],
    key: params.instructionKey ?? 'createMetadataAccountV2',
  };
}

Note that the names of the function and params type are quite lengthy because it follows the convention:

create[InstructionName]InstructionWithSigners

It doesn't bother me too much for the SDK since it'll be wrapped in TransactionBuilders but I'm open to suggestions. 🙂

thlorenz commented 2 years ago

We stashed this feature for now as after discussing with @lorisleiva we realize that there will be lots of edge-cases (i.e. additional signers) which make it hard to create an API that accounts for all of those.

Stashed at stash/ix-with-signers branch.