Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e30548d
patch to compile
SwenSchaeferjohann Oct 23, 2025
58c9743
add mintinstructiondata type, add serde tests. use borsh serde with o…
SwenSchaeferjohann Nov 28, 2025
d3b82d9
replace with borsh
SwenSchaeferjohann Nov 28, 2025
f63a370
clean
SwenSchaeferjohann Nov 28, 2025
25b5769
clean
SwenSchaeferjohann Nov 28, 2025
fb20422
clean
SwenSchaeferjohann Nov 28, 2025
65288ab
remove uploaders, add schema converters
SwenSchaeferjohann Nov 28, 2025
27a25b5
add unit tests for metadata json conv
SwenSchaeferjohann Nov 28, 2025
91af713
include unit tests in js ci cov
SwenSchaeferjohann Nov 28, 2025
059d719
wip
SwenSchaeferjohann Nov 29, 2025
f06aae2
fix token delegate coption parser
SwenSchaeferjohann Nov 29, 2025
8afbe9b
clean
SwenSchaeferjohann Nov 29, 2025
472ab70
fmt and lint
SwenSchaeferjohann Nov 29, 2025
a955e8a
fix v2 stateless.js ci
SwenSchaeferjohann Nov 29, 2025
2fe31f1
try fix forester test with wait_for_queue_space
SwenSchaeferjohann Nov 29, 2025
ce57c41
revert
SwenSchaeferjohann Nov 29, 2025
daf195a
stateless js skip createAccount if v2
SwenSchaeferjohann Nov 29, 2025
4cbcf44
update photon commit to parse-token + rebased to sergey/get_queue_ele…
SwenSchaeferjohann Nov 29, 2025
6b96ae8
add load, decompress2, transferInterface, and various other helpers
SwenSchaeferjohann Nov 30, 2025
9ac8ce3
ts wip
SwenSchaeferjohann Dec 1, 2025
9a1128b
wip
SwenSchaeferjohann Dec 1, 2025
a5cb79d
load_ata test and t22 cov
SwenSchaeferjohann Dec 1, 2025
ce4bfaa
wip
SwenSchaeferjohann Dec 2, 2025
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
21 changes: 19 additions & 2 deletions .github/workflows/js-v2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ jobs:
done
echo "Tests passed on attempt $attempt"

- name: Run compressed-token tests with V2
- name: Run compressed-token legacy tests with V2
run: |
echo "Running compressed-token tests with retry logic (max 2 attempts)..."
echo "Running compressed-token legacy tests with retry logic (max 2 attempts)..."
attempt=1
max_attempts=2
until npx nx test @lightprotocol/compressed-token; do
Expand All @@ -95,6 +95,23 @@ jobs:
done
echo "Tests passed on attempt $attempt"

- name: Run compressed-token ctoken tests with V2
run: |
echo "Running compressed-token ctoken tests with retry logic (max 2 attempts)..."
attempt=1
max_attempts=2
cd js/compressed-token
until LIGHT_PROTOCOL_VERSION=V2 pnpm test:e2e:ctoken:all; do
attempt=$((attempt + 1))
if [ $attempt -gt $max_attempts ]; then
echo "Tests failed after $max_attempts attempts"
exit 1
fi
echo "Attempt $attempt/$max_attempts failed, retrying..."
sleep 5
done
echo "Tests passed on attempt $attempt"

- name: Run sdk-anchor-test TypeScript tests with V2
run: |
npx nx build @lightprotocol/sdk-anchor-test
Expand Down
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/src/commands/create-mint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class CreateMintCommand extends Command {
rpc(),
payer,
mintAuthority,
null,
mintDecimals,
mintKeypair,
);
Expand Down
5 changes: 3 additions & 2 deletions cli/src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ export const SOLANA_VALIDATOR_PROCESS_NAME = "solana-test-validator";
export const LIGHT_PROVER_PROCESS_NAME = "light-prover";
export const INDEXER_PROCESS_NAME = "photon";

export const PHOTON_VERSION = "0.51.0";
export const PHOTON_VERSION = "0.51.1";

// Set these to override Photon requirements with a specific git commit:
export const USE_PHOTON_FROM_GIT = true; // If true, will show git install command instead of crates.io.
export const PHOTON_GIT_REPO = "https://github.com/lightprotocol/photon.git";
export const PHOTON_GIT_COMMIT = "1a785036de52896b68d06413e3b0231122d6aa4a"; // If empty, will use main branch.
export const PHOTON_GIT_COMMIT = "1a3dbe923c2e42eb67c5afdbaf228784dc4f66bf"; // If empty, will use main branch.
// export const PHOTON_GIT_COMMIT = "21c40cb22d7a9cb2635dbd0d04dc807f85da370b"; // If empty, will use main branch.

export const LIGHT_PROTOCOL_PROGRAMS_DIR_ENV = "LIGHT_PROTOCOL_PROGRAMS_DIR";
export const BASE_PATH = "../../bin/";
Expand Down
1 change: 1 addition & 0 deletions cli/src/utils/processPhotonIndexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export async function startIndexer(
if (photonDatabaseUrl) {
args.push("--db-url", photonDatabaseUrl);
}

spawnBinary(INDEXER_PROCESS_NAME, args);
await waitForServers([{ port: indexerPort, path: "/getIndexerHealth" }]);
console.log("Indexer started successfully!");
Expand Down
1 change: 1 addition & 0 deletions cli/test/helpers/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export async function createTestMint(mintKeypair: Keypair) {
rpc,
await getPayer(),
(await getPayer()).publicKey,
null,
9,
mintKeypair,
);
Expand Down
235 changes: 235 additions & 0 deletions js/compressed-token/PAYMENT_MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
# SPL Token to CToken Payment Migration

Mirrors SPL Token's API. Same pattern, same flow.

## TL;DR

```typescript
// SPL Token
import { transfer, getOrCreateAssociatedTokenAccount } from '@solana/spl-token';

// CToken
import {
transferInterface,
getOrCreateAtaInterface,
} from '@lightprotocol/compressed-token';
```

## Action Level

### SPL Token

```typescript
const recipientAta = await getOrCreateAssociatedTokenAccount(
connection,
payer,
mint,
recipient,
);
await transfer(
connection,
payer,
sourceAta,
recipientAta.address,
owner,
amount,
);
```

### CToken

```typescript
const recipientAta = await getOrCreateAtaInterface(rpc, payer, mint, recipient);
await transferInterface(
rpc,
payer,
sourceAta,
recipientAta.address,
owner,
mint,
amount,
);
```

Same two-step pattern. `transferInterface` auto-loads sender's unified balance (cold + SPL + T22).

---

## Instruction Level

### SPL Token

```typescript
import {
createAssociatedTokenAccountIdempotentInstruction,
createTransferInstruction,
getAssociatedTokenAddressSync,
} from '@solana/spl-token';

const sourceAta = getAssociatedTokenAddressSync(mint, sender);
const recipientAta = getAssociatedTokenAddressSync(mint, recipient);

const tx = new Transaction().add(
createAssociatedTokenAccountIdempotentInstruction(
payer,
recipientAta,
recipient,
mint,
),
createTransferInstruction(sourceAta, recipientAta, sender, amount),
);
```

### CToken (sender already hot)

```typescript
import {
getAtaAddressInterface,
createAtaInterfaceIdempotentInstruction,
createTransferInterfaceInstruction,
CTOKEN_PROGRAM_ID,
} from '@lightprotocol/compressed-token';

const sourceAta = getAtaAddressInterface(mint, sender);
const recipientAta = getAtaAddressInterface(mint, recipient);

const tx = new Transaction().add(
createAtaInterfaceIdempotentInstruction(
payer,
recipientAta,
recipient,
mint,
CTOKEN_PROGRAM_ID,
),
createTransferInterfaceInstruction(sourceAta, recipientAta, sender, amount),
);
```

### CToken (sender may be cold - needs loading)

```typescript
import {
loadAtaInstructions,
getAtaAddressInterface,
createAtaInterfaceIdempotentInstruction,
createTransferInterfaceInstruction,
CTOKEN_PROGRAM_ID,
} from '@lightprotocol/compressed-token';

// 1. Derive addresses
const sourceAta = getAtaAddressInterface(mint, sender);
const recipientAta = getAtaAddressInterface(mint, recipient);

// 2. Build load instructions (empty if already hot)
const loadIxs = await loadAtaInstructions(rpc, payer, sourceAta, sender, mint);

// 3. Build transaction
const tx = new Transaction().add(
...loadIxs, // Load sender if cold (wrap SPL/T22, decompress)
createAtaInterfaceIdempotentInstruction(
payer,
recipientAta,
recipient,
mint,
CTOKEN_PROGRAM_ID,
),
createTransferInterfaceInstruction(sourceAta, recipientAta, sender, amount),
);
```

### CToken (sender pre-fetched)

```typescript
import {
getAtaInterface,
loadAtaInstructionsFromInterface,
getAtaAddressInterface,
createAtaInterfaceIdempotentInstruction,
createTransferInterfaceInstruction,
CTOKEN_PROGRAM_ID,
} from '@lightprotocol/compressed-token';

// 1. Pre-fetch sender's unified balance
const senderAtaInfo = await getAtaInterface(rpc, sender, mint);

// 2. Build load instructions from interface (empty if already hot)
const loadIxs = await loadAtaInstructionsFromInterface(
rpc,
payer,
senderAtaInfo,
);

// 3. Derive addresses
const sourceAta = getAtaAddressInterface(mint, sender);
const recipientAta = getAtaAddressInterface(mint, recipient);

// 4. Build transaction
const tx = new Transaction().add(
...loadIxs,
createAtaInterfaceIdempotentInstruction(
payer,
recipientAta,
recipient,
mint,
CTOKEN_PROGRAM_ID,
),
createTransferInterfaceInstruction(sourceAta, recipientAta, sender, amount),
);
```

---

## Instruction Mapping

| SPL Token | CToken |
| --------------------------------------------------- | ----------------------------------------------------------- |
| `getAssociatedTokenAddressSync` | `getAtaAddressInterface` |
| `createAssociatedTokenAccountIdempotentInstruction` | `createAtaInterfaceIdempotentInstruction` |
| `createTransferInstruction` | `createTransferInterfaceInstruction` |
| N/A | `loadAtaInstructions` (fetch + build) |
| N/A | `loadAtaInstructionsFromInterface` (build from pre-fetched) |

---

## Key Differences

| | SPL Token | CToken |
| ------------------- | ---------------------- | --------------------------------------- |
| Recipient ATA | Create before transfer | Create before transfer |
| Sender balance | Single ATA | Unified (cold + SPL + T22 + hot) |
| Loading | N/A | `loadAtaInstructions` or auto in action |
| `destination` param | ATA address | ATA address |

---

## Common Patterns

### Check if loading needed

```typescript
const ata = await getAtaInterface(rpc, owner, mint);
if (ata.isCold) {
// Need to include load instructions
}
```

### Get unified balance

```typescript
const ata = await getAtaInterface(rpc, owner, mint);
const totalBalance = ata.parsed.amount; // All sources combined
```

### Idempotent recipient ATA

Always safe to include - no-op if exists:

```typescript
createAtaInterfaceIdempotentInstruction(
payer,
recipientAta,
recipient,
mint,
CTOKEN_PROGRAM_ID,
);
```
Loading