Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
7cd3470
init
SwenSchaeferjohann Mar 25, 2026
ba4a20d
cleanup
SwenSchaeferjohann Mar 26, 2026
682da16
refactor(token-interface): unify instruction APIs and SPL interface n…
SwenSchaeferjohann Mar 28, 2026
356a650
simplify load
SwenSchaeferjohann Mar 28, 2026
9117742
reduce async
SwenSchaeferjohann Mar 28, 2026
5c11d50
rm redundant rpc
SwenSchaeferjohann Mar 28, 2026
716da39
add tests
SwenSchaeferjohann Mar 28, 2026
280c9b4
dedupe amount helper, unify ix disc
SwenSchaeferjohann Mar 28, 2026
e65a720
upd ci
SwenSchaeferjohann Mar 28, 2026
ec29061
add perms
SwenSchaeferjohann Mar 28, 2026
4f33674
more robust getsplinterface
SwenSchaeferjohann Mar 29, 2026
da5d79c
rm close acc for transfer
SwenSchaeferjohann Mar 29, 2026
9664748
upd typed err in splinterface
SwenSchaeferjohann Mar 29, 2026
3e08610
add test cov for nowrap
SwenSchaeferjohann Mar 29, 2026
288081c
add test cov for race condition on transfer
SwenSchaeferjohann Mar 29, 2026
3858cc9
dedup rpc calls between load and transfer
SwenSchaeferjohann Mar 29, 2026
1123ad2
strict condition for spl interface fetch
SwenSchaeferjohann Mar 29, 2026
cbec066
concurrently fetch getMintDecimals and getSplInterfaces
SwenSchaeferjohann Mar 29, 2026
23fec37
build -> transfer
SwenSchaeferjohann Mar 29, 2026
9aae98d
payer optional for all ixns + test cov
SwenSchaeferjohann Mar 29, 2026
f969172
stricter typing
SwenSchaeferjohann Mar 29, 2026
63ef77b
refactor load
SwenSchaeferjohann Mar 29, 2026
a7336af
format
SwenSchaeferjohann Mar 29, 2026
3b749e1
apply review
SwenSchaeferjohann Mar 30, 2026
07879c4
move nowrap to import path
SwenSchaeferjohann Mar 30, 2026
24a3f32
better nowrap
SwenSchaeferjohann Mar 30, 2026
0d61e03
format
SwenSchaeferjohann Mar 30, 2026
610e25e
tag @internal functions as such
SwenSchaeferjohann Mar 30, 2026
fa6cb0f
add createsplinterface to token-interface sdk
SwenSchaeferjohann Mar 30, 2026
78997e8
add createMint and mintTo
SwenSchaeferjohann Mar 30, 2026
75d4bde
0.1.0, upd pub script, add test cov for new methods
SwenSchaeferjohann Mar 30, 2026
b8a7779
more test cov
SwenSchaeferjohann Mar 30, 2026
fca2552
upd lockfile
SwenSchaeferjohann Mar 30, 2026
d407dca
add more test cov
SwenSchaeferjohann Mar 30, 2026
fdd377b
export wrap and unwrap, add test cov
SwenSchaeferjohann Mar 30, 2026
397ee1e
format
SwenSchaeferjohann Mar 30, 2026
a99d385
fix ci
SwenSchaeferjohann Mar 30, 2026
597ba52
v0.1.1
SwenSchaeferjohann Mar 30, 2026
5551198
export getSplInterfaces
SwenSchaeferjohann Mar 30, 2026
9b2ff26
add mint-to-compressed
SwenSchaeferjohann Mar 30, 2026
5087dae
bump to v0.1.2
SwenSchaeferjohann Mar 30, 2026
eae43fc
use allowOwnerOffCurve=true flag for hi level functions
SwenSchaeferjohann Mar 30, 2026
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
102 changes: 102 additions & 0 deletions .github/workflows/js-token-interface-v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
on:
push:
branches:
- main
pull_request:
branches:
- "*"
types:
- opened
- synchronize
- reopened
- ready_for_review

name: js-token-interface-v2

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
token-interface-js-v2:
name: token-interface-js-v2
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest

services:
redis:
image: redis:8.0.1
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5

env:
LIGHT_PROTOCOL_VERSION: V2
REDIS_URL: redis://localhost:6379
CI: true

steps:
- name: Checkout sources
uses: actions/checkout@v6
with:
submodules: true

- name: Setup and build
uses: ./.github/actions/setup-and-build
with:
skip-components: "redis,disk-cleanup,go"
cache-key: "js"

- name: Build token-interface with V2
run: |
cd js/token-interface
pnpm build:v2

- name: Build CLI
run: |
just cli build

- name: Run token-interface unit tests with V2
run: |
echo "Running token-interface unit tests with retry logic (max 2 attempts)..."
attempt=1
max_attempts=2
until just js test-token-interface-unit-v2; 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 token-interface e2e tests with V2
run: |
echo "Running token-interface e2e tests with retry logic (max 2 attempts)..."
attempt=1
max_attempts=2
until just js test-token-interface-e2e-v2; 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: Display prover logs on failure
if: failure()
run: |
echo "=== Displaying prover logs ==="
find . -path "*/test-ledger/*prover*.log" -type f -exec echo "=== Contents of {} ===" \; -exec cat {} \; -exec echo "=== End of {} ===" \; || echo "No prover logs found"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ api-docs/
# Third-party dependencies
/.local
/.vscode
.cursor/

**/.idea
**/*.iml
Expand Down
2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lightprotocol/zk-compression-cli",
"version": "0.28.1",
"version": "0.28.3",
"description": "ZK Compression: Secure Scaling on Solana",
"maintainers": [
{
Expand Down
2 changes: 1 addition & 1 deletion js/compressed-token/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lightprotocol/compressed-token",
"version": "0.23.1",
"version": "0.23.3",
"description": "JS client to interact with the compressed-token program",
"sideEffects": false,
"main": "dist/cjs/node/index.cjs",
Expand Down
1 change: 0 additions & 1 deletion js/compressed-token/src/v3/instructions/unwrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ export function createUnwrapInstruction(
const SOURCE_INDEX = 2;
const DESTINATION_INDEX = 3;
const POOL_INDEX = 4;
const _SPL_TOKEN_PROGRAM_INDEX = 5;
const LIGHT_TOKEN_PROGRAM_INDEX = 6;

// Unwrap flow: compress from light-token, decompress to SPL
Expand Down
1 change: 0 additions & 1 deletion js/compressed-token/src/v3/instructions/wrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export function createWrapInstruction(
const SOURCE_INDEX = 2;
const DESTINATION_INDEX = 3;
const POOL_INDEX = 4;
const _SPL_TOKEN_PROGRAM_INDEX = 5;
const LIGHT_TOKEN_PROGRAM_INDEX = 6;

const compressions: Compression[] = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,9 @@ describe('LightToken approve/revoke - E2E', () => {
const { blockhash } = await rpc.getLatestBlockhash();
const tx = buildAndSignTx([ix], payer, blockhash, [wrongSigner]);

await expect(sendAndConfirmTx(rpc, tx)).rejects.toThrow();
await expect(sendAndConfirmTx(rpc, tx)).rejects.toThrow(
/owner|authority|custom program error|instruction error/i,
);
}, 60_000);

it('should work with separate fee payer', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ describe('transferDelegatedInterface - failure cases', () => {
undefined,
undefined,
),
).rejects.toThrow();
).rejects.toThrow(/Insufficient delegated balance/);
}, 30_000);

it('rejects transfer from unapproved signer', async () => {
Expand All @@ -112,7 +112,7 @@ describe('transferDelegatedInterface - failure cases', () => {
undefined,
undefined,
),
).rejects.toThrow();
).rejects.toThrow(/Signer is not the owner or a delegate/);
}, 30_000);

it('rejects transfer after revoke', async () => {
Expand All @@ -132,6 +132,6 @@ describe('transferDelegatedInterface - failure cases', () => {
undefined,
undefined,
),
).rejects.toThrow();
).rejects.toThrow(/Signer is not the owner or a delegate/);
}, 60_000);
});
14 changes: 13 additions & 1 deletion js/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,34 @@ default:
build:
cd stateless.js && pnpm build
cd compressed-token && pnpm build
cd token-interface && pnpm build

test: test-stateless test-compressed-token
test: test-stateless test-compressed-token test-token-interface

test-stateless:
cd stateless.js && pnpm test

test-compressed-token:
cd compressed-token && pnpm test

test-token-interface:
cd token-interface && pnpm test

test-token-interface-unit-v2:
cd token-interface && pnpm test:unit:all

test-token-interface-e2e-v2:
cd token-interface && pnpm test:e2e:all

test-compressed-token-unit-v2:
cd compressed-token && pnpm test:unit:all:v2

lint:
cd stateless.js && pnpm lint
cd compressed-token && pnpm lint
cd token-interface && pnpm lint

format:
cd stateless.js && pnpm format
cd compressed-token && pnpm format
cd token-interface && pnpm format
2 changes: 1 addition & 1 deletion js/stateless.js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lightprotocol/stateless.js",
"version": "0.23.1",
"version": "0.23.3",
"description": "JavaScript API for Light & ZK Compression",
"sideEffects": false,
"main": "dist/cjs/node/index.cjs",
Expand Down
10 changes: 10 additions & 0 deletions js/token-interface/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 80,
"useTabs": false,
"tabWidth": 4,
"bracketSpacing": true,
"arrowParens": "avoid"
}
105 changes: 105 additions & 0 deletions js/token-interface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# `@lightprotocol/token-interface`

Payments-focused helpers for Light rent-free token flows.

Use this when you want SPL-style transfers with unified sender handling:

- sender side auto wraps/loads into light ATA
- recipient ATA can be light (default), SPL, or Token-2022 via `tokenProgram`

## RPC client (required)

All builders expect `createRpc()` from `@lightprotocol/stateless.js`.

```ts
import { createRpc } from '@lightprotocol/stateless.js';

// Add this to your client. It is a superset of web3.js Connection RPC plus Light APIs.
const rpc = createRpc();
// Optional: createRpc(clusterUrl)
```

## Canonical for Kit users

Use `createTransferInstructionPlan` from `/kit`.

```ts
import { createTransferInstructionPlan } from '@lightprotocol/token-interface/kit';

const transferPlan = await createTransferInstructionPlan({
rpc,
payer: payer.publicKey,
mint,
sourceOwner: sender.publicKey,
authority: sender.publicKey,
recipient: customer.publicKey,
// Optional destination program:
// tokenProgram: TOKEN_PROGRAM_ID
amount: 25n,
});
```

If you prefer Kit instruction arrays instead of plans:

```ts
import { createTransferInstructions } from '@lightprotocol/token-interface/kit';
```

## Canonical for web3.js users

Use `createTransferInstructions` from the root export.

```ts
import { createTransferInstructions } from '@lightprotocol/token-interface';

const instructions = await createTransferInstructions({
rpc,
payer: payer.publicKey,
mint,
sourceOwner: sender.publicKey,
authority: sender.publicKey,
recipient: customer.publicKey,
amount: 25n,
});

// add memo if needed, then build/sign/send transaction
```

## Raw single-instruction helpers

Use these when you want manual orchestration:

```ts
import {
createAtaInstruction,
createLoadInstructions,
createTransferCheckedInstruction,
} from '@lightprotocol/token-interface/instructions';
```

## No-wrap instruction-flow builders (advanced)

If you explicitly want to disable automatic sender wrapping, use the dedicated
`/nowrap` entrypoint with the same function names:

```ts
import { createTransferInstructions } from '@lightprotocol/token-interface/nowrap';
```

## Read account

```ts
import { getAta } from '@lightprotocol/token-interface';

const account = await getAta({ rpc, owner: customer.publicKey, mint });
console.log(account.amount, account.hotAmount, account.compressedAmount);
```

## Important rules

- Only one compressed sender account is loaded per call; smaller ones are ignored for that call.
- Transfer always builds checked semantics.
- Canonical builders always use wrap-enabled sender setup (`createTransferInstructions`, `createLoadInstructions`, `createApproveInstructions`, `createRevokeInstructions`).
- If sender SPL/T22 balances are wrapped by the flow, source SPL/T22 ATAs are not auto-closed.
- Recipient ATA is derived from `(recipient, mint, tokenProgram)`; default is light token program.
- Recipient-side load is still intentionally disabled.
Loading
Loading