Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/rust-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,20 @@ jobs:
- name: Test
working-directory: pinocchio/swap
run: cargo test-sbf -p pinocchio-swap -- --test-threads=1

check-toolkits:
name: sponsor-rent-top-ups
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup environment
uses: ./.github/actions/setup
with:
example: toolkits/sponsor-rent-top-ups/rust
solana-cli-version: ${{ env.SOLANA_CLI_VERSION }}
rust-toolchain: ${{ env.RUST_TOOLCHAIN }}

- name: Check
working-directory: toolkits/sponsor-rent-top-ups/rust
run: cargo check
23 changes: 23 additions & 0 deletions .github/workflows/sync-docs-snippets.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Sync Docs Snippets

on:
push:
branches: [main]
paths:
- "typescript-client/**"
- "rust-client/**"
- "programs/anchor/basic-instructions/**"
- "programs/anchor/basic-macros/**"
- "toolkits/sponsor-rent-top-ups/**"

jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- name: Dispatch to docs-v2
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.DOCS_DISPATCH_TOKEN }}
repository: Lightprotocol/docs-v2
event-type: sync-examples-light-token
client-payload: '{"commit": "${{ github.sha }}"}'
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@
cli/accounts/
bugs.md

.surfpool/
.surfpool/
.entire/
**/Cargo.lock
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## What this repo is

Light Token example programs demonstrating rent-free token vaults using Light Protocol on Solana. Contains Anchor programs (escrow, fundraiser, token-swap, light-token-minter), a shared test utilities crate, TypeScript client examples, and toolkits (payments, streaming).
Light Token example programs demonstrating rent-free token vaults using Light Protocol on Solana. Contains Anchor programs (escrow, fundraiser, token-swap, light-token-minter, pinocchio-swap), a shared test utilities crate, TypeScript and Rust client examples, and toolkits (payments, streaming, sign-with-privy, sponsor-rent-top-ups).

## Build and test

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Light token is a high-performance token standard that reduces the cost of mint a
| [Payments and Wallets](toolkits/payments-and-wallets/) | All you need for wallet integrations and payment flows. Minimal API differences to SPL. |
| [Streaming Tokens](toolkits/streaming-tokens/) | Stream mint events using Laserstream |
| [Sign with Privy](toolkits/sign-with-privy/) | Light-token operations signed with Privy wallets (Node.js + React) |
| [Sponsor Rent Top-Ups](toolkits/sponsor-rent-top-ups/) | Sponsor rent top-ups for users by setting your application as the fee payer |

## Client Examples

Expand Down Expand Up @@ -60,8 +61,9 @@ Light token is a high-performance token standard that reduces the cost of mint a
| [escrow](programs/anchor/escrow) | Peer-to-peer light-token swap with offer/accept flow |
| [fundraiser](programs/anchor/fundraiser) | Token fundraiser with target, deadline, and refunds |
| [light-token-minter](programs/anchor/light-token-minter) | Create light-mints with metadata, mint tokens |
| [token-swap](programs/anchor/token-swap) | AMM with liquidity pools and swaps |
| [token-swap](programs/anchor/token-swap) | AMM with liquidity pools and swaps (Anchor) |
| [cp-swap-reference](https://github.com/Lightprotocol/cp-swap-reference/tree/954f679699dbdfe9711308c8ae9fbd21e69db8aa) | Fork of Raydium AMM that creates markets without paying rent-exemption |
| [pinocchio-swap](pinocchio/swap) | AMM with liquidity pools and swaps (Pinocchio) |
| [create-and-transfer](programs/anchor/create-and-transfer) | Create account via macro and transfer via CPI |

### Macros
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"toolkits/payments-and-wallets",
"toolkits/sign-with-privy/react",
"toolkits/sign-with-privy/nodejs",
"toolkits/sign-with-privy/scripts"
"toolkits/sign-with-privy/scripts",
"toolkits/sponsor-rent-top-ups/typescript"
],
"scripts": {
"toolkit:payments": "npm run -w toolkits/payments-and-wallets"
Expand Down
4 changes: 2 additions & 2 deletions programs/anchor/basic-instructions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ You can replace spl_token with light_token instructions as you need. The API is
- **[approve](approve/src/lib.rs)** - Approve delegate
- **[burn](burn/src/lib.rs)** - Burn tokens
- **[close](close/src/lib.rs)** - Close token account
- **[create-associated-token-account](create-ata/src/lib.rs)** - Create associated light-token account
- **[create-associated-token-account](create-associated-token-account/src/lib.rs)** - Create associated light-token account
- **[create-mint](create-mint/src/lib.rs)** - Create light-token mint
- **[create-token-account](create-token-account/src/lib.rs)** - Create light-token account
- **[freeze](freeze/src/lib.rs)** - Freeze token account
Expand All @@ -23,7 +23,7 @@ The examples use pure CPI calls which you can combine with existing and / or [li

```bash
# for localnet
npm i -g @lightprotocol/zk-compression-cli@alpha
npm i -g @lightprotocol/zk-compression-cli@beta
```

```bash
Expand Down
4 changes: 2 additions & 2 deletions programs/anchor/basic-macros/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
| | |
|---------|--------|
| [**counter**](counter) | Create PDA with sponsored rent-exemption |
| [**create-associated-token-account**](create-ata) | Create associated light-token account |
| [**create-associated-token-account**](create-associated-token-account) | Create associated light-token account |
| [**create-mint**](create-mint) | Create light-token mint |
| [**create-token-account**](create-token-account) | Create light-token account |

Expand All @@ -17,7 +17,7 @@ For existing programs, you can replace spl_token with light_token instructions a

```bash
# for localnet
npm i -g @lightprotocol/zk-compression-cli@alpha
npm i -g @lightprotocol/zk-compression-cli@beta
```

```bash
Expand Down
6 changes: 6 additions & 0 deletions toolkits/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ Light-token operations signed with [Privy](https://privy.io) wallets. Server-sid

[Rust program example to stream mint events](streaming-tokens/) of the Light-Token Program.

### Sponsor Rent Top-Ups

Sponsor rent top-ups for users by setting your application as the fee payer.
- **[TypeScript](sponsor-rent-top-ups/typescript/)** - Sponsored Light transfer
- **[Rust](sponsor-rent-top-ups/rust/)** - Sponsored Light transfer

## Documentation

Learn more [about Light-Token here](https://www.zkcompression.com/light-token/welcome).
39 changes: 39 additions & 0 deletions toolkits/sponsor-rent-top-ups/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Sponsor Rent Top-Ups

Light Token sponsors rent-exemption for Solana accounts and keeps them active through periodic top-ups paid by the fee payer. Set your application as the fee payer to abstract away holding SOL from your users — a rent-free experience similar to transaction fee sponsorship.

- **[TypeScript](typescript/)** — Sponsored transfer using `@lightprotocol/compressed-token`
- **[Rust](rust/)** — Sponsored transfer using `light-token`

Set the `feePayer` field to the public key of the account that will pay the top-ups. Any account that signs the transaction can be the fee payer.

1. Build the transfer instruction with your server as the `feePayer`
2. User signs to authorize the transfer (no SOL needed)
3. Fee payer covers rent top-ups when the transaction lands

> You can set the fee payer to any signing account on any transaction with Light Token.

## Source files

- **[sponsor-top-ups.ts](typescript/sponsor-top-ups.ts)** / **[sponsor-top-ups.rs](rust/sponsor-top-ups.rs)** — Sponsored transfer: creates recipient ATA, transfers tokens with sponsor as fee payer
- **[setup.ts](typescript/setup.ts)** / **[setup.rs](rust/setup.rs)** — Test setup: creates SPL mint, mints tokens, wraps into Light

## Setup

```bash
cd typescript
npm install
```

For localnet:

```bash
npm i -g @lightprotocol/zk-compression-cli@beta
light test-validator
npm run sponsor-top-ups
```

## Documentation

- [Sponsor rent top-ups](https://www.zkcompression.com/light-token/toolkits/sponsor-top-ups)
- [Light Token overview](https://www.zkcompression.com/light-token/welcome)
19 changes: 19 additions & 0 deletions toolkits/sponsor-rent-top-ups/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "sponsor-rent-top-ups"
version = "0.1.0"
edition = "2021"
publish = false

[[bin]]
name = "sponsor-top-ups"
path = "sponsor-top-ups.rs"

[dependencies]
light-token = "0.23.0"
light-program-test = "0.23.0"
light-client = "0.23.0"
anchor-spl = "0.31"
solana-sdk = "2"
spl-token = "7"
tokio = { version = "1", features = ["full"] }
blake3 = "=1.5.5"
156 changes: 156 additions & 0 deletions toolkits/sponsor-rent-top-ups/rust/setup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Test setup: creates an SPL mint, funds the sponsor, wraps into Light.
// In production the mint already exists (e.g. USDC) and the sender
// already holds Light tokens.

use anchor_spl::{
associated_token::spl_associated_token_account,
token::{spl_token, Mint as SPLMint},
};
use light_client::rpc::Rpc;
use light_program_test::LightProgramTest;
use light_token::{
instruction::{
get_associated_token_address, CreateAssociatedTokenAccount, SplInterface,
TransferInterface, LIGHT_TOKEN_PROGRAM_ID,
},
spl_interface::CreateSplInterfacePda,
};
#[allow(deprecated)]
use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, system_instruction};

pub struct SetupResult {
pub mint: Pubkey,
pub sender_ata: Pubkey,
}

async fn setup_spl_mint(rpc: &mut LightProgramTest, payer: &Keypair, decimals: u8) -> Pubkey {
let mint_keypair = Keypair::new();
let mint = mint_keypair.pubkey();

let mint_rent = rpc
.get_minimum_balance_for_rent_exemption(SPLMint::LEN)
.await
.unwrap();

let create_mint_instruction = system_instruction::create_account(
&payer.pubkey(),
&mint,
mint_rent,
SPLMint::LEN as u64,
&spl_token::ID,
);

let init_mint_instruction = spl_token::instruction::initialize_mint(
&spl_token::ID,
&mint,
&payer.pubkey(),
None,
decimals,
)
.unwrap();

rpc.create_and_send_transaction(
&[create_mint_instruction, init_mint_instruction],
&payer.pubkey(),
&[payer, &mint_keypair],
)
.await
.unwrap();

let create_interface_instruction =
CreateSplInterfacePda::new(payer.pubkey(), mint, spl_token::ID, false).instruction();

rpc.create_and_send_transaction(&[create_interface_instruction], &payer.pubkey(), &[payer])
.await
.unwrap();

mint
}

async fn setup_spl_associated_token_account(
rpc: &mut LightProgramTest,
payer: &Keypair,
mint: &Pubkey,
owner: &Pubkey,
amount: u64,
) -> Pubkey {
let associated_token_account =
spl_associated_token_account::get_associated_token_address(owner, mint);

let create_ata_instruction =
spl_associated_token_account::instruction::create_associated_token_account_idempotent(
&payer.pubkey(),
owner,
mint,
&spl_token::ID,
);

let mut instructions = vec![create_ata_instruction];

if amount > 0 {
let mint_instruction = spl_token::instruction::mint_to(
&spl_token::ID,
mint,
&associated_token_account,
&payer.pubkey(),
&[],
amount,
)
.unwrap();
instructions.push(mint_instruction);
}

rpc.create_and_send_transaction(&instructions, &payer.pubkey(), &[payer])
.await
.unwrap();

associated_token_account
}

pub async fn setup(
rpc: &mut LightProgramTest,
sponsor: &Keypair,
sender: &Keypair,
) -> Result<SetupResult, Box<dyn std::error::Error>> {
let decimals = 6u8;
let amount = 1_000_000u64;

let mint = setup_spl_mint(rpc, sponsor, decimals).await;
let sponsor_spl_ata = setup_spl_associated_token_account(
rpc, sponsor, &mint, &sponsor.pubkey(), amount,
).await;
let (interface_pda, interface_bump) =
light_token::spl_interface::find_spl_interface_pda_with_index(&mint, 0, false);

let sender_ata = get_associated_token_address(&sender.pubkey(), &mint);
let create_ata_ix =
CreateAssociatedTokenAccount::new(sponsor.pubkey(), sender.pubkey(), mint).instruction()?;
rpc.create_and_send_transaction(&[create_ata_ix], &sponsor.pubkey(), &[sponsor])
.await?;

let spl_interface = SplInterface {
mint,
spl_token_program: spl_token::ID,
spl_interface_pda: interface_pda,
spl_interface_pda_bump: interface_bump,
};

let wrap_ix = TransferInterface {
source: sponsor_spl_ata,
destination: sender_ata,
amount,
decimals,
mint,
authority: sponsor.pubkey(),
payer: sponsor.pubkey(),
spl_interface: Some(spl_interface),
source_owner: spl_token::ID,
destination_owner: LIGHT_TOKEN_PROGRAM_ID,
}
.instruction()?;

rpc.create_and_send_transaction(&[wrap_ix], &sponsor.pubkey(), &[sponsor])
.await?;

Ok(SetupResult { mint, sender_ata })
}
Loading