Skip to content
Closed
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
898 changes: 426 additions & 472 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ anchor-spl = "=0.31.1"
# Anchor compatibility
borsh = "0.10.0"

# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

# Macro helpers
proc-macro2 = "1.0"
quote = "1.0"
Expand Down
5 changes: 1 addition & 4 deletions cli/src/commands/compress-spl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class CompressSplCommand extends Command {
const toPublicKey = new PublicKey(to);
const mintPublicKey = new PublicKey(mint);
const payer = defaultSolanaWalletKeypair();
const tokenProgramId = await CompressedTokenProgram.get_mint_program_id(
const tokenProgramId = await CompressedTokenProgram.getMintProgramId(
mintPublicKey,
rpc(),
);
Expand All @@ -73,9 +73,6 @@ class CompressSplCommand extends Command {
payer,
sourceAta,
toPublicKey,
undefined,
undefined,
tokenProgramId,
);

loader.stop(false);
Expand Down
5 changes: 1 addition & 4 deletions cli/src/commands/decompress-spl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class DecompressSplCommand extends Command {
const toPublicKey = new PublicKey(to);
const mintPublicKey = new PublicKey(mint);
const payer = defaultSolanaWalletKeypair();
const tokenProgramId = await CompressedTokenProgram.get_mint_program_id(
const tokenProgramId = await CompressedTokenProgram.getMintProgramId(
mintPublicKey,
rpc(),
);
Expand All @@ -75,9 +75,6 @@ class DecompressSplCommand extends Command {
amount,
payer,
recipientAta.address,
undefined,
undefined,
tokenProgramId,
);

loader.stop(false);
Expand Down
2 changes: 1 addition & 1 deletion cli/src/commands/init/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
kebabCase,
snakeCase,
} from "case-anything";
import { execSync } from "child_process";

export default class InitCommand extends Command {
static description = "Initialize a compressed account project.";

Expand Down
2 changes: 1 addition & 1 deletion cli/src/commands/start-prover/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class StartProver extends Command {

const proverPort = flags["prover-port"] || 3001;
const force = flags["force"] || false;
const redisUrl = flags["redisUrl"] || process.env.REDIS_URL;
const redisUrl = flags["redisUrl"] || process.env.REDIS_URL || undefined;
await startProver(
proverPort,
flags["run-mode"],
Expand Down
10 changes: 6 additions & 4 deletions examples/browser/nextjs/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import {
bn,
buildTx,
confirmTx,
defaultTestStateTreeAccounts,
selectMinCompressedSolAccountsForTransfer,
createRpc,
selectStateTreeInfo,
} from '@lightprotocol/stateless.js';

// Default styles that can be overridden by your app
Expand All @@ -36,7 +36,10 @@ const SendButton: FC = () => {
const { publicKey, sendTransaction } = useWallet();

const onClick = useCallback(async () => {
const connection = await createRpc();
const connection = createRpc();
const stateTreeInfo = selectStateTreeInfo(
await connection.getStateTreeInfos(),
);

if (!publicKey) throw new WalletNotConnectedError();

Expand All @@ -51,7 +54,7 @@ const SendButton: FC = () => {
payer: publicKey,
toAddress: publicKey,
lamports: 1e8,
outputStateTree: defaultTestStateTreeAccounts().merkleTree,
outputStateTreeInfo: stateTreeInfo,
});
const compressInstructions = [
ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000 }),
Expand Down Expand Up @@ -109,7 +112,6 @@ const SendButton: FC = () => {
toAddress: recipient,
lamports: 1e7,
inputCompressedAccounts: selectedAccounts,
outputStateTrees: [defaultTestStateTreeAccounts().merkleTree],
recentValidityProof: compressedProof,
recentInputStateRootIndices: rootIndices,
});
Expand Down
96 changes: 95 additions & 1 deletion js/compressed-token/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,97 @@
## [0.22.0]

- `CreateMint` action now allows passing a non-payer mint and freeze authority.
- More efficient computebudgets for actions.
- Better DX: Parameter lookup in call signatures of CompressedTokenProgram instructions
- QoL: improved typedocs.

## [0.21.0]

#### Breaking Changes

This release has several breaking changes which improve protocol
scalability. Please reach out to the [team](https://t.me/swen_light) if you need help migrating.

### Migration guide: Compress

**Old Code** (remove this)

```typescript
const activeStateTrees = await connection.getCachedActiveStateTreeInfo();
const { tree } = pickRandomTreeAndQueue(activeStateTrees);
// ...
```

**New Code**

```typescript
// ...
const treeInfos = await rpc.getStateTreeInfos();
const treeInfo = selectStateTreeInfo(treeInfos);

const infos = await getTokenPoolInfos(rpc, mint);
const info = selectTokenPoolInfo(infos);

const compressIx = await CompressedTokenProgram.compress({
// ...
outputStateTreeInfo,
tokenPoolInfo,
});
```

### Migration guide: Decompress

```typescript
// ...
// new code:
const infos = await getTokenPoolInfos(rpc, mint);
const selectedInfos = selectTokenPoolInfosForDecompression(
tokenPoolInfos,
amount,
);

const ix = await CompressedTokenProgram.decompress({
// ...
tokenPoolInfos: selectedTokenPoolInfos,
});
```

### Overview

- new type: TokenPoolInfo
- Instruction Changes:

- `compress`, `mintTo`, `approveAndMintTo`, `compressSplTokenAccount` now require valid TokenPoolInfo
- `decompress` now requires an array of one or more TokenPoolInfos.
- `decompress`, `transfer` now do not allow state tree overrides.

- Action Changes:

- Removed optional tokenProgramId: PublicKey
- removed optional merkleTree: PublicKey
- removed optional outputStateTree: PublicKey
- added optional stateTreeInfo: StateTreeInfo
- added optional tokenPoolInfo: TokenPoolInfo

- new instructions:
- `approve`, `revoke`: delegated transfer support.
- `addTokenPools`: you can now register additional token pool pdas. Use
this if you need very high concurrency.

### Why the Changes are helpful

`getStateTreeInfos()` retrieves relevant info about all active state trees.

When building a transaction you can now pick a random treeInfo via `selectStateTreeInfo(infos)`.

This lets you and other apps execute more transactions within Solana's write lock
limits.

The same applies to `getTokenPoolInfos`. When you compress or decompress SPL
tokens, a tokenpool gets write-locked. If you need additional per-block write
lock capacity, you can register and sample additional (up to 4) tokenpool
accounts.

## [0.20.5-0.20.9] - 2025-02-24

### Changed
Expand All @@ -17,7 +111,7 @@
### Added

- `selectSmartCompressedTokenAccountsForTransfer` and
`selectSmartCompressedTokenAccountsForTransferorPartial`
`selectSmartCompressedTokenAccountsForTransferOrPartial`

### Changed

Expand Down
17 changes: 11 additions & 6 deletions 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.20.9",
"version": "0.21.0",
"description": "JS client to interact with the compressed-token program",
"sideEffects": false,
"main": "dist/cjs/node/index.cjs",
Expand Down Expand Up @@ -82,20 +82,25 @@
"test:unit:all": "EXCLUDE_E2E=true vitest run",
"test-all:verbose": "vitest run --reporter=verbose",
"test-validator": "./../../cli/test_bin/run test-validator --prover-run-mode rpc",
"test-validator-skip-prover": "./../../cli/test_bin/run test-validator --skip-prover",
"test:e2e:create-mint": "pnpm test-validator && NODE_OPTIONS='--trace-deprecation' vitest run tests/e2e/create-mint.test.ts --reporter=verbose",
"test:e2e:layout": "vitest run tests/e2e/layout.test.ts --reporter=verbose",
"test:e2e:layout": "vitest run tests/e2e/layout.test.ts --reporter=verbose --bail=1",
"test:e2e:select-accounts": "vitest run tests/e2e/select-accounts.test.ts --reporter=verbose",
"test:e2e:create-token-pool": "pnpm test-validator && vitest run tests/e2e/create-token-pool.test.ts",
"test:e2e:mint-to": "pnpm test-validator && vitest run tests/e2e/mint-to.test.ts --reporter=verbose",
"test:e2e:approve-and-mint-to": "pnpm test-validator && vitest run tests/e2e/approve-and-mint-to.test.ts --reporter=verbose",
"test:e2e:mint-to": "pnpm test-validator && vitest run tests/e2e/mint-to.test.ts --reporter=verbose --bail=1",
"test:e2e:approve-and-mint-to": "pnpm test-validator && vitest run tests/e2e/approve-and-mint-to.test.ts --reporter=verbose --bail=1",
"test:e2e:merge-token-accounts": "pnpm test-validator && vitest run tests/e2e/merge-token-accounts.test.ts --reporter=verbose",
"test:e2e:transfer": "pnpm test-validator && vitest run tests/e2e/transfer.test.ts --reporter=verbose",
"test:e2e:transfer": "pnpm test-validator && vitest run tests/e2e/transfer.test.ts --reporter=verbose --bail=1",
"test:e2e:delegate": "pnpm test-validator && vitest run tests/e2e/delegate.test.ts --reporter=verbose --bail=1",
"test:e2e:transfer-delegated": "pnpm test-validator && vitest run tests/e2e/transfer-delegated.test.ts --reporter=verbose --bail=1",
"test:e2e:compress": "pnpm test-validator && vitest run tests/e2e/compress.test.ts --reporter=verbose",
"test:e2e:compress-spl-token-account": "pnpm test-validator && vitest run tests/e2e/compress-spl-token-account.test.ts --reporter=verbose",
"test:e2e:decompress": "pnpm test-validator && vitest run tests/e2e/decompress.test.ts --reporter=verbose",
"test:e2e:decompress-delegated": "pnpm test-validator && vitest run tests/e2e/decompress-delegated.test.ts --reporter=verbose",
"test:e2e:rpc-token-interop": "pnpm test-validator && vitest run tests/e2e/rpc-token-interop.test.ts --reporter=verbose",
"test:e2e:rpc-multi-trees": "pnpm test-validator && vitest run tests/e2e/rpc-multi-trees.test.ts --reporter=verbose",
"test:e2e:all": "pnpm test-validator && vitest run tests/e2e/create-mint.test.ts && vitest run tests/e2e/mint-to.test.ts && vitest run tests/e2e/transfer.test.ts && vitest run tests/e2e/compress.test.ts && vitest run tests/e2e/compress-spl-token-account.test.ts && vitest run tests/e2e/decompress.test.ts && vitest run tests/e2e/create-token-pool.test.ts && vitest run tests/e2e/approve-and-mint-to.test.ts && vitest run tests/e2e/rpc-token-interop.test.ts && vitest run tests/e2e/rpc-multi-trees.test.ts && vitest run tests/e2e/layout.test.ts && vitest run tests/e2e/select-accounts.test.ts",
"test:e2e:multi-pool": "pnpm test-validator && vitest run tests/e2e/multi-pool.test.ts --reporter=verbose",
"test:e2e:all": "pnpm test-validator && vitest run tests/e2e/create-mint.test.ts && vitest run tests/e2e/mint-to.test.ts && vitest run tests/e2e/transfer.test.ts && vitest run tests/e2e/delegate.test.ts && vitest run tests/e2e/transfer-delegated.test.ts && vitest run tests/e2e/multi-pool.test.ts && vitest run tests/e2e/decompress-delegated.test.ts && pnpm test-validator-skip-prover && vitest run tests/e2e/compress.test.ts && vitest run tests/e2e/compress-spl-token-account.test.ts && vitest run tests/e2e/decompress.test.ts && vitest run tests/e2e/create-token-pool.test.ts && vitest run tests/e2e/approve-and-mint-to.test.ts && vitest run tests/e2e/rpc-token-interop.test.ts && vitest run tests/e2e/rpc-multi-trees.test.ts && vitest run tests/e2e/layout.test.ts && vitest run tests/e2e/select-accounts.test.ts",
"pull-idl": "../../scripts/push-compressed-token-idl.sh",
"build": "rimraf dist && pnpm build:bundle",
"build:bundle": "rollup -c",
Expand Down
3 changes: 1 addition & 2 deletions js/compressed-token/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,9 @@ const rolls = (fmt, env) => ({
env === 'browser' ? nodePolyfills() : undefined,
terser({
compress: {
drop_console: true,
drop_console: false,
drop_debugger: true,
passes: 3,
pure_funcs: ['console.log', 'console.error', 'console.warn'],
booleans_as_integers: true,
keep_fargs: false,
keep_fnames: false,
Expand Down
67 changes: 37 additions & 30 deletions js/compressed-token/src/actions/approve-and-mint-to.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,53 @@ import {
buildAndSignTx,
Rpc,
dedupeSigner,
pickRandomTreeAndQueue,
selectStateTreeInfo,
toArray,
TreeInfo,
} from '@lightprotocol/stateless.js';
import { CompressedTokenProgram } from '../program';
import { getOrCreateAssociatedTokenAccount } from '@solana/spl-token';

import {
getTokenPoolInfos,
selectTokenPoolInfo,
TokenPoolInfo,
} from '../utils/get-token-pool-infos';

/**
* Mint compressed tokens to a solana address from an external mint authority
*
* @param rpc Rpc to use
* @param payer Payer of the transaction fees
* @param mint Mint for the account
* @param destination Address of the account to mint to
* @param authority Minting authority
* @param amount Amount to mint
* @param merkleTree State tree account that the compressed tokens should be
* part of. Defaults to random public state tree account.
* @param confirmOptions Options for confirming the transaction
* @param rpc Rpc to use
* @param payer Fee payer
* @param mint SPL Mint address
* @param toPubkey Address of the account to mint to
* @param authority Minting authority
* @param amount Amount to mint
* @param outputStateTreeInfo Optional: State tree account that the compressed
* tokens should be inserted into. Defaults to a
* shared state tree account.
* @param tokenPoolInfo Optional: Token pool info.
* @param confirmOptions Options for confirming the transaction
*
* @return Signature of the confirmed transaction
*/
export async function approveAndMintTo(
rpc: Rpc,
payer: Signer,
mint: PublicKey,
destination: PublicKey,
toPubkey: PublicKey,
authority: Signer,
amount: number | BN,
merkleTree?: PublicKey,
outputStateTreeInfo?: TreeInfo,
tokenPoolInfo?: TokenPoolInfo,
confirmOptions?: ConfirmOptions,
tokenProgramId?: PublicKey,
): Promise<TransactionSignature> {
tokenProgramId = tokenProgramId
? tokenProgramId
: await CompressedTokenProgram.get_mint_program_id(mint, rpc);
outputStateTreeInfo =
outputStateTreeInfo ??
selectStateTreeInfo(await rpc.getStateTreeInfos());
tokenPoolInfo =
tokenPoolInfo ??
selectTokenPoolInfo(await getTokenPoolInfos(rpc, mint));

const authorityTokenAccount = await getOrCreateAssociatedTokenAccount(
rpc,
Expand All @@ -54,40 +67,34 @@ export async function approveAndMintTo(
undefined,
undefined,
confirmOptions,
tokenProgramId,
tokenPoolInfo.tokenProgram,
);

if (!merkleTree) {
const stateTreeInfo = await rpc.getCachedActiveStateTreeInfo();
const { tree } = pickRandomTreeAndQueue(stateTreeInfo);
merkleTree = tree;
}

const ixs = await CompressedTokenProgram.approveAndMintTo({
feePayer: payer.publicKey,
mint,
authority: authority.publicKey,
authorityTokenAccount: authorityTokenAccount.address,
amount,
toPubkey: destination,
merkleTree,
tokenProgramId,
toPubkey,
outputStateTreeInfo,
tokenPoolInfo,
});

const { blockhash } = await rpc.getLatestBlockhash();
const additionalSigners = dedupeSigner(payer, [authority]);

const tx = buildAndSignTx(
[
ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000 }),
ComputeBudgetProgram.setComputeUnitLimit({
units: 150_000 + toArray(amount).length * 20_000,
}),
...ixs,
],
payer,
blockhash,
additionalSigners,
);

const txId = await sendAndConfirmTx(rpc, tx, confirmOptions);

return txId;
return await sendAndConfirmTx(rpc, tx, confirmOptions);
}
Loading