> ## Documentation Index
> Fetch the complete documentation index at: https://build.onswig.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Transferring stuff with Swig

This guide walks through different ways to perform transfers using Swig, progressing from basic to more advanced patterns.
You can find all example code in the [swig-ts/examples/basic/classic](https://github.com/anagrambuild/swig-ts/tree/main/examples/basic/classic) directory or for kit refer to this directory [swig-ts/examples/basic/kit](https://github.com/anagrambuild/swig-ts/tree/main/examples/basic/kit).

<Tip>Examples with svm in the file name use LiteSVM for testing. Those without svm in the file name use a local validator. Which you can run with the `bun start-validator` command.</Tip>

## Basic Transfer with Ed25519 Keys

The simplest way to use Swig is with standard Ed25519 keypairs. Here are the key parts:

<Tabs>
  <Tab title="Classic">
    ```ts theme={null}
    // Key Setup - Using standard Solana keypairs
    const userRootKeypair = Keypair.generate();
    const dappAuthorityKeypair = Keypair.generate();

    // Create a new Swig account with root authority
    const rootActions = Actions.set().all().get();
    const createSwigInstruction = await getCreateSwigInstruction({
      payer: userRootKeypair.publicKey,
      authorityInfo: createEd25519AuthorityInfo(userRootKeypair.publicKey),
      id,
      actions: rootActions,
    });

    // Fetch the Swig account and get the wallet address
    const swig = await fetchSwig(connection, swigAccountAddress);
    const swigWalletAddress = await getSwigWalletAddress(swig);

    // Add a new authority with a spending limit
    const dappActions = Actions.set().solLimit({ amount: BigInt(0.1 * LAMPORTS_PER_SOL) }).get();
    const addAuthorityInstructions = await getAddAuthorityInstructions(
      swig,
      rootRole.id,
      createEd25519AuthorityInfo(dappAuthorityKeypair.publicKey),
      dappActions,
    );

    // Transfer SOL with Swig authority (use swigWalletAddress, not swigAccountAddress)
    const transferIx = SystemProgram.transfer({
      fromPubkey: swigWalletAddress,
      toPubkey: dappAuthorityKeypair.publicKey,
      lamports: 0.1 * LAMPORTS_PER_SOL,
    });

    const signedInstructions = await getSignInstructions(
      swig,
      dappRole.id,
      [transferIx]
    );

    await sendTransaction(connection, signedInstructions, dappAuthorityKeypair);
    ```
  </Tab>

  <Tab title="Kit">
    ```ts theme={null}
    // Key Setup - Using @solana/kit KeyPairSigner
    const userRoot = await generateKeyPairSigner();
    const dappAuthority = await generateKeyPairSigner();

    // Create Swig with Ed25519 authority
    const rootActions = Actions.set().all().get();
    const createSwigInstruction = await getCreateSwigInstruction({
      payer: userRoot.address,
      authorityInfo: createEd25519AuthorityInfo(userRoot.address),
      id,
      actions: rootActions,
    });

    // Fetch the Swig account and get the wallet address
    const swig = await fetchSwig(connection.rpc, swigAccountAddress);
    const swigWalletAddress = await getSwigWalletAddress(swig);

    // Add a dapp authority with spending limit
    const dappActions = Actions.set().solLimit({ amount: BigInt(0.1 * LAMPORTS_PER_SOL) }).get();
    const addAuthorityInstructions = await getAddAuthorityInstructions(
      swig,
      rootRole.id,
      createEd25519AuthorityInfo(dappAuthority.address),
      dappActions,
    );

    // Transfer SOL (use swigWalletAddress, not swigAccountAddress)
    const transferInstruction = getSolTransferInstruction({
      fromAddress: swigWalletAddress,
      toAddress: dappAuthority.address,
      lamports: 0.1 * LAMPORTS_PER_SOL,
    });

    const signedInstructions = await getSignInstructions(
      swig,
      dappRole.id,
      [transferInstruction],
    );

    await sendTransaction(connection, signedInstructions, dappAuthority);
    ```
  </Tab>
</Tabs>

You can find the complete example in `transfer-local.ts` and `transfer-svm.ts` for local and LiteSVM environments respectively.

***

## Using Secp256k1 Keys (EVM Compatible)

For EVM compatibility, Swig supports Secp256k1 keys. Note the key differences from the Ed25519 example:

<Tabs>
  <Tab title="Classic">
    ```ts theme={null}
    // Key Setup - Using Ethereum wallet
    const userWallet = Wallet.generate(); // 👈 EVM wallet instead of Solana Keypair

    // Connect to network and create payers
    const connection = new Connection('http://localhost:8899', 'confirmed');
    const payer = Keypair.generate();
    await connection.requestAirdrop(payer.publicKey, LAMPORTS_PER_SOL);
    const signer = Keypair.generate();
    await connection.requestAirdrop(signer.publicKey, LAMPORTS_PER_SOL);

    // Find Swig PDA by ID
    const id = Uint8Array.from(Array(32).fill(1)); // use any 32-byte identifier
    const swigAccountAddress = findSwigPda(id);

    // Create Swig with Secp256k1 authority
    const rootActions = Actions.set().all().get();
    const createSwigInstruction = await getCreateSwigInstruction({
      authorityInfo: createSecp256k1AuthorityInfo(userWallet.getPublicKey()),
      id,
      payer: payer.publicKey,
      actions: rootActions,
    });

    // Fetch the Swig account and get the wallet address
    const swig = await fetchSwig(connection, swigAccountAddress);
    const swigWalletAddress = await getSwigWalletAddress(swig);

    // Prepare transfer (use swigWalletAddress, not swigAccountAddress)
    const transfer = SystemProgram.transfer({
      fromPubkey: swigWalletAddress,
      toPubkey: dappTreasury,
      lamports: 0.1 * LAMPORTS_PER_SOL,
    });

    // Prepare transfer
    const transfer = SystemProgram.transfer({
      fromPubkey: swigAddress,
      toPubkey: dappTreasury,
      lamports: 0.1 * LAMPORTS_PER_SOL,
    });

    // Sign using the Secp256k1 private key
    const signingFn = getSigningFnForSecp256k1PrivateKey(
      userWallet.getPrivateKey(),
    );
    const slot = await connection.getSlot('finalized');

    const signedTransfer = await getSignInstructions(
      swig,
      rootRole.id,
      [transfer],
      false,
      {
        currentSlot: BigInt(slot),
        signingFn,
        payer: signer.publicKey,
      },
    );
    ```
  </Tab>

  <Tab title="Kit">
    ```ts theme={null}
    // Key Setup - Using Ethereum wallet
    const userWallet = Wallet.generate(); // 👈 EVM wallet instead of Solana keypair

    // RPC Setup
    const rpc = createSolanaRpc('http://localhost:8899');
    const rpcSubscriptions = createSolanaRpcSubscriptions('ws://localhost:8900');
    const connection = { rpc, rpcSubscriptions };

    // Solana accounts for fees and transactions
    const payer = await generateKeyPairSigner();
    const signer = await generateKeyPairSigner();
    const dappTreasury = await generateKeyPairSigner();

    // Find Swig PDA by ID
    const id = Uint8Array.from(Array(32).fill(1));
    const swigAccountAddress = await findSwigPda(id);

    // Create Swig with Secp256k1 authority
    const rootActions = Actions.set().all().get();
    const createSwigIx = await getCreateSwigInstruction({
      authorityInfo: createSecp256k1AuthorityInfo(userWallet.getPublicKey()),
      id,
      payer: payer.address,
      actions: rootActions,
    });

    // Fetch the Swig account and get the wallet address
    const swig = await fetchSwig(connection.rpc, swigAccountAddress);
    const swigWalletAddress = await getSwigWalletAddress(swig);

    // Prepare transfer instruction (use swigWalletAddress, not swigAccountAddress)
    const transfer = {
      programAddress: SYSTEM_PROGRAM_ADDRESS,
      accounts: [
        {
          address: swigWalletAddress,
          role: AccountRole.WRITABLE_SIGNER,
        },
        {
          address: dappTreasury.address,
          role: AccountRole.WRITABLE,
        },
      ],
      data: new Uint8Array(
        getTransferSolInstructionDataEncoder().encode({
          amount: 100_000_000, // 0.1 SOL
        }),
      ),
    };

    // Sign using Secp256k1 private key
    const currentSlot = await rpc.getSlot().send();
    const signingFn = getSigningFnForSecp256k1PrivateKey(userWallet.getPrivateKey());

    const signIx = await getSignInstructions(swig, rootRole.id, [transfer], false, {
      payer: signer.address,
      currentSlot,
      signingFn,
    });
    ```
  </Tab>
</Tabs>

Check out `transfer-local-secp.ts` and `transfer-svm-secp.ts` for complete examples.

***

## Using Viem for Ethereum Wallet Integration

For a more complete Ethereum wallet integration using Viem:

```ts theme={null}
// Using Viem account
const privateKeyAccount = privateKeyToAccount(userWallet.getPrivateKeyString());

const swigAccountAddress = findSwigPda(id);
const rootActions = Actions.set().all().get();

const createSwigInstruction = await getCreateSwigInstruction({
  authorityInfo: createSecp256k1AuthorityInfo(privateKeyAccount.publicKey),
  id,
  payer: transactionPayer.publicKey,
  actions: rootActions,
});

sendSVMTransaction(svm, [createSwigInstruction], transactionPayer);

let swig = fetchSwig(svm, swigAccountAddress);
const swigWalletAddress = await getSwigWalletAddress(swig);
svm.airdrop(swigWalletAddress, BigInt(LAMPORTS_PER_SOL));

let rootRole = swig.findRolesBySecp256k1SignerAddress(privateKeyAccount.address)[0];
if (!rootRole) throw new Error('Role not found for authority');

const viemSign: SigningFn = async (message: Uint8Array) => {
  const sig = await privateKeyAccount.sign({ hash: keccak256(message) });
  return { signature: hexToBytes(sig) };
};

const transfer = SystemProgram.transfer({
  fromPubkey: swigAddress,
  toPubkey: dappTreasury,
  lamports: 0.1 * LAMPORTS_PER_SOL,
});

let signTransfer = await getSignInstructions(swig, rootRole.id, [transfer], false, {
  currentSlot: svm.getClock().slot,
  signingFn: viemSign,
  payer: transactionPayer.publicKey,
});

sendSVMTransaction(svm, signTransfer, transactionPayer);

const viemSignWithPrefix: SigningFn = async (message: Uint8Array) => {
  const prefix = getEvmPersonalSignPrefix(message.length);
  const prefixedMessage = new Uint8Array(prefix.length + message.length);

  prefixedMessage.set(prefix);
  prefixedMessage.set(message, prefix.length);

  const sig = await privateKeyAccount.sign({ hash: keccak256(prefixedMessage) });
  return { signature: hexToBytes(sig), prefix };
};

signTransfer = await getSignInstructions(swig, rootRole.id, [transfer], false, {
  currentSlot: svm.getClock().slot,
  signingFn: viemSignWithPrefix,
  payer: transactionPayer.publicKey,
});

sendSVMTransaction(svm, signTransfer, transactionPayer);

const viemSignMessage: SigningFn = async (message: Uint8Array) => {
  const sig = await privateKeyAccount.signMessage({ message: { raw: message } });
  return {
    signature: hexToBytes(sig),
    prefix: getEvmPersonalSignPrefix(message.length),
  };
};

signTransfer = await getSignInstructions(swig, rootRole.id, [transfer], false, {
  currentSlot: svm.getClock().slot,
  signingFn: viemSignMessage,
  payer: transactionPayer.publicKey,
});

sendSVMTransaction(svm, signTransfer, transactionPayer);
```

See `transfer-svm-viem.ts` for the full working code.

***

## Using Sessions for Enhanced Security

Sessions allow limited, temporary authority:

### Ed25519 Sessions

<Tabs>
  <Tab title="Classic">
    ```ts theme={null}
    // Key Setup
    const userRootKeypair = Keypair.generate();
    const dappSessionKeypair = Keypair.generate();

    // Create Swig with Session Authority
    const createSwigIx = await getCreateSwigInstruction({
      id,
      authorityInfo: createEd25519SessionAuthorityInfo(
        userRootKeypair.publicKey,
        100n, // session duration in slots
      ),
      actions: rootActions,
      payer: userRootKeypair.publicKey,
    });

    // Create Session for dApp
    const createSessionIx = await getCreateSessionInstructions(
      swig,
      rootRole.id,
      dappSessionKeypair.publicKey,
      50n, // session duration
    );

    // Fetch the Swig account and get the wallet address
    const swigWalletAddress = await getSwigWalletAddress(swig);

    // Find and use session role
    const sessionRole = swig.findRoleBySessionKey(dappSessionKeypair.publicKey);
    if (!sessionRole || !sessionRole.isSessionBased()) {
      throw new Error('Invalid session role');
    }

    // Create transfer using swigWalletAddress
    const transfer = SystemProgram.transfer({
      fromPubkey: swigWalletAddress,
      toPubkey: dappTreasury,
      lamports: 0.1 * LAMPORTS_PER_SOL,
    });

    // Sign with session key
    const signedTransfer = await getSignInstructions(
      swig,
      sessionRole.id,
      [transfer],
      false,
      { payer: dappSessionKeypair.publicKey },
    );
    ```
  </Tab>

  <Tab title="Kit">
    ```ts theme={null}
    // Key Setup
    const userRootKeypair = await generateKeyPairSigner();
    const dappSessionKeypair = await generateKeyPairSigner();

    // Create Swig with Session Authority
    const createSwigIx = await getCreateSwigInstruction({
      id,
      authorityInfo: createEd25519SessionAuthorityInfo(
        userRootKeypair.address,
        100n, // session duration in slots
      ),
      actions: rootActions,
      payer: userRootKeypair.address,
    });

    // Create Session for dApp
    const createSessionIx = await getCreateSessionInstructions(
      swig,
      rootRole.id,
      dappSessionKeypair.address,
      50n, // session duration
    );

    // Fetch the Swig account and get the wallet address
    const swigWalletAddress = await getSwigWalletAddress(swig);

    // Find and use session role
    const sessionRole = swig.findRolesByEd25519SignerPk(dappSessionKeypair.address)[0];
    if (!sessionRole || !sessionRole.isSessionBased()) {
      throw new Error('Invalid session role');
    }

    // Create transfer using swigWalletAddress
    const transferIx = getSolTransferInstruction({
      fromAddress: swigWalletAddress,
      toAddress: dappTreasury.address,
      lamports: 0.1 * LAMPORTS_PER_SOL,
    });

    // Sign with session key
    const signedTransferIx = await getSignInstructions(
      swig,
      sessionRole.id,
      [transferIx],
      false,
      { payer: dappSessionKeypair.address },
    );
    ```
  </Tab>
</Tabs>

### Secp256k1 Sessions

<Tabs>
  <Tab title="Classic">
    ```ts theme={null}
    // Key Setup - EVM wallet with session capability
    const userWallet = Wallet.generate();
    const userRootKeypair = Keypair.generate();
    const dappSessionKeypair = Keypair.generate();

    // Create Swig with Secp256k1 Session Authority
    const createSwigInstruction = await getCreateSwigInstruction({
      authorityInfo: createSecp256k1SessionAuthorityInfo(
        userWallet.getPublicKey(),
        100n, // max session duration in slots
      ),
      id,
      payer: userRootKeypair.publicKey,
      actions: rootActions,
    });

    // Create Session with EVM signing
    const signingFn = getSigningFnForSecp256k1PrivateKey(
      userWallet.getPrivateKeyString(),
    );
    const currentSlot = await connection.getSlot('confirmed');

    const sessionInstructions = await getCreateSessionInstructions(
      swig,
      rootRole.id,
      dappSessionKeypair.publicKey,
      50n, // session duration
      {
        currentSlot: BigInt(currentSlot),
        signingFn,
        payer: userRootKeypair.publicKey,
      },
    );

    // Fetch the Swig account and get the wallet address
    const swigWalletAddress = await getSwigWalletAddress(swig);

    // Find and use session role
    const sessionRole = swig.findRoleBySessionKey(dappSessionKeypair.publicKey);
    if (!sessionRole) throw new Error('Session role not found');

    // Create transfer using swigWalletAddress
    const transferIx = SystemProgram.transfer({
      fromPubkey: swigWalletAddress,
      toPubkey: dappTreasury,
      lamports: 0.1 * LAMPORTS_PER_SOL,
    });

    // Sign with session key
    const signTransfer = await getSignInstructions(
      swig,
      sessionRole.id,
      [transferIx],
      false,
      {
        currentSlot: BigInt(currentSlot),
        signingFn,
        payer: dappSessionKeypair.publicKey,
      },
    );
    ```
  </Tab>

  <Tab title="Kit">
    ```ts theme={null}
    // Key Setup - EVM wallet with session capability
    const userWallet = Wallet.generate();
    const userRootKeypair = await generateKeyPairSigner();
    const dappSessionKeypair = await generateKeyPairSigner();

    // Create Swig with Secp256k1 Session Authority
    const authorityInfo = createSecp256k1SessionAuthorityInfo(
      userWallet.getPublicKey(),
      100n, // max session duration in slots
    );

    const createSwigInstruction = await getCreateSwigInstruction({
      authorityInfo,
      id,
      payer: userRootKeypair.address,
      actions: rootActions,
    });

    // Create Session with EVM signing
    const signingFn = getSigningFnForSecp256k1PrivateKey(userWallet.getPrivateKey());
    const currentSlot = await rpc.getSlot().send();

    const sessionInstructions = await getCreateSessionInstructions(
      swig,
      rootRole.id,
      dappSessionKeypair.address,
      50n, // session duration
      { 
        payer: userRootKeypair.address,
        currentSlot,
        signingFn,
      },
    );

    // Fetch the Swig account and get the wallet address
    const swigWalletAddress = await getSwigWalletAddress(swig);

    // Find and use session role
    const sessionRole = swig.findRoleBySessionKey(dappSessionKeypair.address);
    if (!sessionRole) throw new Error('Session role not found');

    // Create transfer using swigWalletAddress
    const transferIx = getSolTransferInstruction({
      fromAddress: swigWalletAddress,
      toAddress: dappTreasury.address,
      lamports: 0.1 * LAMPORTS_PER_SOL,
    });

    // Sign with session key
    const signTransfer = await getSignInstructions(
      swig,
      sessionRole.id,
      [transferIx],
      false,
      {
        payer: dappSessionKeypair.address,
        currentSlot,
        signingFn,
      },
    );
    ```
  </Tab>
</Tabs>

***

## Key aspects of sessions:

* Can be created with any supported authority type (Ed25519, Secp256k1)
* Limited duration specified in slots
* Restricted set of actions (defined by sessionActions)
* Perfect for dApp integrations where temporary access is needed

You can find complete session examples in (we have just mentioned a few you can find all of them labelled a specific way but it will contain the word "sessions" in the file name):

* `transfer-local-session.ts` (Ed25519)
* `transfer-svm-session.ts` (Ed25519)
* `transfer-svm-secp-session.ts` (Secp256k1)

For more detailed information about sessions, check out the [Creating Sessions Guide](/examples/sessions).

***

## Testing Environment Options

These examples can be run in different environments:

**Local Validator**: Use the `transfer-local-*` examples

* Requires running a local Solana validator
* Real blockchain environment
* Good for final testing

**LiteSVM**: Use the `transfer-svm-*` examples

* No validator required
* Instant transaction confirmation
* Perfect for rapid development
* Simulates the Solana runtime

**Devnet**: All examples work with devnet

* Change connection URL to `https://api.devnet.solana.com`
* Real network environment
* Free to use with airdropped SOL

LiteSVM is recommended for initial development as it provides the fastest feedback loop. Move to local validator or devnet for more thorough testing.
