In this tutorial, we’ll learn how to add multiple authorities to a Swig account with different permission sets. We’ll create a root authority that can manage other authorities, then add two specialized authorities: one for SOL spending and another for token management.

Prerequisites

Before starting this tutorial:
  1. Make sure you’re in the examples/classic/transfer/tutorial directory for the classic version and examples/kit/transfer/tutorial for kit
  2. Ensure your local validator is running with:
    bun start-validator
    

Quick Review

In Tutorial 1, we learned how to create a basic Swig account. Now we’ll expand on that by managing multiple authorities with different permissions.
mkdir tutorial && touch tutorial-2.ts
Open that file up in your favorite editor, or AI overlord super virtual reality goggles. Lets make a function called addNewAuthority

Example

import {
  Connection,
  Keypair,
  LAMPORTS_PER_SOL,
  PublicKey,
  Transaction,
  sendAndConfirmTransaction,
} from "@solana/web3.js";

import {
  Actions,
  createEd25519AuthorityInfo,
  fetchSwig,
  findSwigPda,
  getAddAuthorityInstructions,
  getCreateSwigInstruction,
} from "@swig-wallet/classic";

import chalk from "chalk";

async function createSwigAccount(connection: Connection, user: Keypair) {
  try {
    const id = new Uint8Array(32);
    crypto.getRandomValues(id);
    const swigAddress = findSwigPda(id);
    const rootAuthorityInfo = createEd25519AuthorityInfo(user.publicKey);
    const rootActions = Actions.set().manageAuthority().get();

    const createSwigIx = await getCreateSwigInstruction({
      payer: user.publicKey,
      id,
      actions: rootActions,
      authorityInfo: rootAuthorityInfo,
    });

    const transaction = new Transaction().add(createSwigIx);
    const signature = await sendAndConfirmTransaction(connection, transaction, [
      user,
    ]);

    console.log(
      chalk.green("✓ Swig account created at:"),
      chalk.cyan(swigAddress.toBase58()),
    );
    console.log(chalk.blue("Transaction signature:"), chalk.cyan(signature));
    return swigAddress;
  } catch (error) {
    console.error(
      chalk.red("✗ Error creating Swig account:"),
      chalk.red(error),
    );
    throw error;
  }
}

async function addNewAuthority(
  connection: Connection,
  rootUser: Keypair,
  newAuthority: Keypair,
  swigAddress: PublicKey,
  actions: any,
  description: string,
) {
  try {
    const swig = await fetchSwig(connection, swigAddress);

    const rootRole = swig.findRolesByEd25519SignerPk(rootUser.publicKey)[0];
    if (!rootRole) {
      throw new Error("Root role not found for authority");
    }

    const addAuthorityInstructions = await getAddAuthorityInstructions(
      swig,
      rootRole.id,
      createEd25519AuthorityInfo(newAuthority.publicKey),
      actions,
    );

    const transaction = new Transaction().add(...addAuthorityInstructions);
    await sendAndConfirmTransaction(connection, transaction, [rootUser]);
    console.log(
      chalk.green(`✓ New ${description} authority added:`),
      chalk.cyan(newAuthority.publicKey.toBase58()),
    );
  } catch (error) {
    console.error(
      chalk.red(`✗ Error adding ${description} authority:`),
      chalk.red(error),
    );
    throw error;
  }
}

(async () => {
  console.log(chalk.blue('🚀 Starting tutorial - Adding Multiple Authorities'));

  // Connect to local Solana network
  const connection = new Connection("http://localhost:8899", "confirmed");

  const rootUser = Keypair.generate();
  console.log(
    chalk.green("👤 Root user public key:"),
    chalk.cyan(rootUser.publicKey.toBase58()),
  );

  const airdrop = await connection.requestAirdrop(
    rootUser.publicKey,
    100 * LAMPORTS_PER_SOL,
  );
  const blockhash = await connection.getLatestBlockhash();
  await connection.confirmTransaction({
    signature: airdrop,
    blockhash: blockhash.blockhash,
    lastValidBlockHeight: blockhash.lastValidBlockHeight,
  });

  console.log(chalk.yellow('\n📝 Creating Swig account...'));
  const swigAddress = await createSwigAccount(connection, rootUser);

  const spendingAuthority = Keypair.generate();
  console.log(
    chalk.green("\n👥 Spending authority public key:"),
    chalk.cyan(spendingAuthority.publicKey.toBase58()),
  );

  // Create the token authority
  const tokenAuthority = Keypair.generate();
  console.log(
    chalk.green("👥 Token authority public key:"),
    chalk.cyan(tokenAuthority.publicKey.toBase58()),
  );

  // Add some delay to ensure the Swig account is created
  await new Promise((resolve) => setTimeout(resolve, 2000));

  const spendingActions = Actions.set()
    .solLimit({ amount: BigInt(0.1 * LAMPORTS_PER_SOL) })
    .get();

  await addNewAuthority(
    connection,
    rootUser,
    spendingAuthority,
    swigAddress,
    spendingActions,
    'spending',
  );

  const tokenMint = Keypair.generate().publicKey;
  const tokenActions = Actions.set()
    .tokenLimit({ mint: tokenMint, amount: BigInt(1000000) })
    .get();

  await addNewAuthority(
    connection,
    rootUser,
    tokenAuthority,
    swigAddress,
    tokenActions,
    'token',
  );

  const swig = await fetchSwig(connection, swigAddress);

  console.log(chalk.blue('\n📊 Authority Permissions:'));

  const spendingRole = swig.findRolesByEd25519SignerPk(
    spendingAuthority.publicKey,
  )[0];
  console.log(chalk.yellow('Spending Authority:'));
  console.log(
    '- Can spend SOL:',
    chalk.green(
      spendingRole.actions.canSpendSol(BigInt(0.1 * LAMPORTS_PER_SOL)),
    ),
  );
  console.log(
    '- Can spend tokens:',
    chalk.red(spendingRole.actions.canSpendToken(tokenMint, BigInt(1000000))),
  );

  const tokenRole = swig.findRolesByEd25519SignerPk(
    tokenAuthority.publicKey,
  )[0];
  console.log(chalk.yellow('\nToken Authority:'));
  console.log(
    '- Can spend SOL:',
    chalk.red(tokenRole.actions.canSpendSol(BigInt(0.1 * LAMPORTS_PER_SOL))),
  );
  console.log(
    '- Can spend tokens:',
    chalk.green(tokenRole.actions.canSpendToken(tokenMint, BigInt(1000000))),
  );

  console.log(chalk.green('\n✨ Tutorial completed successfully!'));
  console.log(
    chalk.yellow('🔍 Check out your transaction on Solana Explorer:'),
  );
  console.log(
    chalk.cyan(
      `https://explorer.solana.com/address/${swigAddress}?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899`,
    ),
  );
})();

Code Breakdown

Let’s examine the key parts of tutorial-2.ts:

1. Creating a Root Authority

const rootActions = Actions.set().manageAuthority().get();
const createSwigIx = await getCreateSwigInstruction({
  payer: user.publicKey,
  id,
  actions: rootActions,
  authorityInfo: createEd25519AuthorityInfo(user.publicKey),
});

const transaction = new Transaction().add(createSwigIx);
const signature = await sendAndConfirmTransaction(connection, transaction, [user]);
This creates a Swig account where the root authority has permission to manage other authorities. We specifically use .manageAuthority() to limit this authority to only managing other authorities.

2. Adding a SOL Spending Authority

Don’t forget that the full list of actions an authority can have is here.
const spendingActions = Actions.set()
  .solLimit({ amount: BigInt(0.1 * LAMPORTS_PER_SOL) })
  .get();

await addNewAuthority(
  connection,
  rootUser,
  spendingAuthority,
  swigAddress,
  spendingActions,
  "spending"
);
This creates an authority that:
  • Can only spend SOL (no token permissions)
  • Has a spending limit of 0.1 SOL
  • Cannot manage other authorities

3. Adding a Token Authority

const tokenMint = Keypair.generate().publicKey; // Example token mint
const tokenActions = Actions.set()
  .tokenLimit({ mint: tokenMint, amount: BigInt(1000000) })
  .get();

await addNewAuthority(
  connection,
  rootUser,
  tokenAuthority,
  swigAddress,
  tokenActions,
  "token"
);
This creates an authority that:
  • Can only manage tokens for a specific mint
  • Has a token spending limit of 1,000,000 units
  • Cannot spend SOL or manage other authorities

Understanding the Output

When you run the tutorial, you’ll see:
  1. The root user’s public key
  2. The Swig account address
  3. Two new authority public keys
  4. A permission summary showing what each authority can and cannot do
The tutorial provides links to view your transactions on Solana Explorer (using your local validator).

Key Concepts

  • Root Authority: An authority with management permissions that can add or remove other authorities
  • Specialized Authorities: Authorities with specific, limited permissions (e.g., SOL spending or token management)
  • Permission Limits:
    • SOL limits are set in lamports (1 SOL = 1,000,000,000 lamports)
    • Token limits are set based on the token’s decimals
  • Action Sets: Define what an authority can do using methods like:
    • .manageAuthority()
    • .solLimit()
    • .tokenLimit()

Running the Tutorial

Execute the tutorial with:
bun run ./tutorial/tutorial-2.ts
You nailed it , first try!! You now have a multi authority Swig, free energy and perpetual motion next.