Skip to content

Commit 9d0e87d

Browse files
author
tilo-14
committed
add indexing tokens warm-up examples
Replace inline load-ata code on for-streaming-tokens page with warm-up snippets sourced from examples-light-token. Shows detect cold + decompress + trade pattern for indexers.
1 parent b2e6f73 commit 9d0e87d

4 files changed

Lines changed: 172 additions & 132 deletions

File tree

light-token/toolkits/for-streaming-tokens.mdx

Lines changed: 24 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -5,158 +5,50 @@ description: "Light token accounts follow the same layout as SPL-token accounts,
55
keywords: ["streaming tokens for solana apps", "scalable token distribution on solana", "token streaming for developers"]
66
---
77

8-
import ToolkitsSetup from "/snippets/setup/toolkits-setup.mdx";
8+
import WarmUpAction from "/snippets/code-snippets/light-token/warm-up/warm-up-action.mdx";
9+
import WarmUpInstruction from "/snippets/code-snippets/light-token/warm-up/warm-up-instruction.mdx";
910

10-
When a market becomes inactive, its token accounts and related PDAs will be compressed - their state is committed and effectively frozen until a client decompresses it.
11+
When a market becomes inactive, its token accounts and related PDAs will
12+
be compressed automatically (cold storage).
13+
The state is cryptographically preserved on the Solana ledger.
1114
While compressed, pure on-chain lookups will return uninitialized.
1215

13-
Your indexer should keep tracking, quoting, and routing markets even if the on-chain account shows `is_initialized: false`, `is_compressed: true`. To trade a cold market, the first client must prepend an idempotent decompress "warm up" instruction.
16+
Your indexer should keep tracking, quoting, and routing markets even if the
17+
on-chain account shows `is_initialized: false`, `is_compressed: true`.
18+
To trade a cold market, the first client must prepend an
19+
idempotent decompress "warm up" instruction.
1420

1521
<Info>
1622
Find the source code
17-
[here](https://github.com/Lightprotocol/light-protocol/blob/main/js/compressed-token/tests/e2e/load-ata-standard.test.ts).
23+
[here](https://github.com/Lightprotocol/examples-light-token/tree/main/toolkits/indexing-tokens).
1824
</Info>
1925

20-
## Setup
21-
22-
<ToolkitsSetup />
23-
2426
<Steps>
2527
<Step>
26-
### Load Compressed Tokens to Hot Balance
27-
28-
<CodeGroup>
29-
```typescript Action
30-
import { Keypair } from "@solana/web3.js";
31-
import { createRpc, bn } from "@lightprotocol/stateless.js";
32-
import {
33-
createMint,
34-
mintTo,
35-
loadAta,
36-
getAssociatedTokenAddressInterface,
37-
} from "@lightprotocol/compressed-token";
38-
39-
async function main() {
40-
const rpc = createRpc();
41-
const payer = Keypair.generate();
42-
await rpc.requestAirdrop(payer.publicKey, 10e9);
43-
44-
const owner = Keypair.generate();
45-
await rpc.requestAirdrop(owner.publicKey, 1e9);
46-
47-
const mintAuthority = Keypair.generate();
48-
const mintKeypair = Keypair.generate();
49-
const { mint } = await createMint(
50-
rpc,
51-
payer,
52-
mintAuthority.publicKey,
53-
9,
54-
mintKeypair,
55-
);
56-
57-
await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000));
58-
59-
// Get light-token ATA address
60-
const tokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey);
61-
62-
// Load compressed tokens to hot balance
63-
// Creates ATA if needed, returns null if nothing to load
64-
const signature = await loadAta(rpc, tokenAta, owner, mint, payer);
65-
66-
if (signature) {
67-
console.log("Loaded tokens to hot balance");
68-
console.log("Transaction:", signature);
69-
} else {
70-
console.log("Nothing to load");
71-
}
72-
}
73-
74-
main().catch(console.error);
75-
```
76-
77-
```typescript Instruction
78-
import { Keypair, ComputeBudgetProgram } from "@solana/web3.js";
79-
import {
80-
createRpc,
81-
bn,
82-
buildAndSignTx,
83-
sendAndConfirmTx,
84-
dedupeSigner,
85-
} from "@lightprotocol/stateless.js";
86-
import {
87-
createMint,
88-
mintTo,
89-
createLoadAtaInstructions,
90-
getAssociatedTokenAddressInterface,
91-
} from "@lightprotocol/compressed-token";
92-
93-
async function main() {
94-
const rpc = createRpc();
95-
const payer = Keypair.generate();
96-
await rpc.requestAirdrop(payer.publicKey, 10e9);
97-
98-
const owner = Keypair.generate();
99-
await rpc.requestAirdrop(owner.publicKey, 1e9);
100-
101-
const mintAuthority = Keypair.generate();
102-
const mintKeypair = Keypair.generate();
103-
const { mint } = await createMint(
104-
rpc,
105-
payer,
106-
mintAuthority.publicKey,
107-
9,
108-
mintKeypair
109-
);
110-
111-
await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000));
112-
113-
// Get light-token ATA address
114-
const tokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey);
115-
116-
// Create load instructions
117-
const ixs = await createLoadAtaInstructions(
118-
rpc,
119-
tokenAta,
120-
owner.publicKey,
121-
mint,
122-
payer.publicKey
123-
);
124-
125-
if (ixs.length === 0) {
126-
console.log("Nothing to load");
127-
return;
128-
}
129-
130-
// Build, sign, and send transaction
131-
const { blockhash } = await rpc.getLatestBlockhash();
132-
const additionalSigners = dedupeSigner(payer, [owner]);
133-
134-
const tx = buildAndSignTx(
135-
[ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 }), ...ixs],
136-
payer,
137-
blockhash,
138-
additionalSigners
139-
);
140-
141-
const signature = await sendAndConfirmTx(rpc, tx);
142-
console.log("Loaded tokens to hot balance");
143-
console.log("Transaction:", signature);
144-
}
28+
### Warm up a cold market and trade
14529

146-
main().catch(console.error);
147-
```
30+
Prepend idempotent decompress instructions before your trade.
31+
`loadAta` returns null and `createLoadAtaInstructions` returns an empty array
32+
when the account is already hot, so this pattern is safe to use unconditionally.
14833

149-
</CodeGroup>
34+
<Tabs>
35+
<Tab title="Action">
36+
<WarmUpAction />
37+
</Tab>
38+
<Tab title="Instruction">
39+
<WarmUpInstruction />
40+
</Tab>
41+
</Tabs>
15042

15143
</Step>
15244
</Steps>
15345

154-
# Stream Light-Mint Accounts
46+
# Stream light-mint accounts
15547

15648
<Card
15749
title="Toolkit to stream light-mints"
15850
icon="chevron-right"
15951
color="#0066ff"
16052
href="/light-token/toolkits/for-streaming-mints"
16153
horizontal
162-
/>
54+
/>

scripts/copy-light-token-snippets.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ RECIPES=("create-mint" "create-ata" "mint-to" "transfer-interface" "load-ata" "w
1313
wrap_typescript() {
1414
local input_file="$1"
1515
local output_file="$2"
16+
mkdir -p "$(dirname "$output_file")"
1617
echo '```typescript' > "$output_file"
1718
cat "$input_file" >> "$output_file"
1819
echo '```' >> "$output_file"
@@ -40,6 +41,19 @@ for recipe in "${RECIPES[@]}"; do
4041
fi
4142
done
4243

44+
# Indexing toolkit snippets
45+
INDEXING_DIR="/home/tilo/Workspace/examples-light-token/toolkits/indexing-tokens"
46+
echo "Processing: indexing-tokens toolkit"
47+
48+
for variant in "warm-up-action" "warm-up-instruction"; do
49+
src="$INDEXING_DIR/$variant.ts"
50+
if [ -f "$src" ]; then
51+
wrap_typescript "$src" "$SNIPPETS_DIR/warm-up/$variant.mdx"
52+
else
53+
echo " WARNING: Not found - $src"
54+
fi
55+
done
56+
4357
echo ""
4458
echo "Done! Created snippets in: $SNIPPETS_DIR"
4559
echo ""
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
```typescript
2+
import "dotenv/config";
3+
import { Keypair } from "@solana/web3.js";
4+
import { createRpc } from "@lightprotocol/stateless.js";
5+
import {
6+
createMintInterface,
7+
createAtaInterface,
8+
mintToCompressed,
9+
loadAta,
10+
transferInterface,
11+
getAssociatedTokenAddressInterface,
12+
} from "@lightprotocol/compressed-token";
13+
import { homedir } from "os";
14+
import { readFileSync } from "fs";
15+
16+
// devnet:
17+
// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`;
18+
// const rpc = createRpc(RPC_URL);
19+
// localnet:
20+
const rpc = createRpc();
21+
22+
const payer = Keypair.fromSecretKey(
23+
new Uint8Array(
24+
JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")),
25+
),
26+
);
27+
28+
(async function () {
29+
// Inactive Light Tokens are cryptographically preserved on the Solana ledger
30+
// as compressed tokens (cold storage)
31+
// Setup: Get compressed tokens in light-token associated token account
32+
const { mint } = await createMintInterface(rpc, payer, payer, null, 9);
33+
await mintToCompressed(rpc, payer, mint, payer, [{ recipient: payer.publicKey, amount: 1000n }]);
34+
35+
const recipient = Keypair.generate();
36+
await createAtaInterface(rpc, payer, mint, recipient.publicKey);
37+
38+
const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey);
39+
const recipientAta = getAssociatedTokenAddressInterface(mint, recipient.publicKey);
40+
41+
// Warm up: load compressed tokens to associated token account
42+
// Returns null if already hot
43+
await loadAta(rpc, senderAta, payer, mint, payer);
44+
45+
// Transfer tokens from hot balance
46+
const tx = await transferInterface(
47+
rpc,
48+
payer,
49+
senderAta,
50+
mint,
51+
recipientAta,
52+
payer,
53+
500n,
54+
);
55+
56+
console.log("Tx:", tx);
57+
})();```
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
```typescript
2+
import "dotenv/config";
3+
import { Keypair } from "@solana/web3.js";
4+
import {
5+
createRpc,
6+
buildAndSignTx,
7+
sendAndConfirmTx,
8+
} from "@lightprotocol/stateless.js";
9+
import {
10+
createMintInterface,
11+
createAtaInterface,
12+
mintToCompressed,
13+
createLoadAtaInstructions,
14+
createTransferInterfaceInstruction,
15+
getAssociatedTokenAddressInterface,
16+
} from "@lightprotocol/compressed-token";
17+
import { homedir } from "os";
18+
import { readFileSync } from "fs";
19+
20+
// devnet:
21+
// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`;
22+
// const rpc = createRpc(RPC_URL);
23+
// localnet:
24+
const rpc = createRpc();
25+
26+
const payer = Keypair.fromSecretKey(
27+
new Uint8Array(
28+
JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")),
29+
),
30+
);
31+
32+
(async function () {
33+
// Inactive Light Tokens are cryptographically preserved on the Solana ledger
34+
// as compressed tokens (cold storage)
35+
// Setup: Get compressed tokens in light-token associated token account
36+
const { mint } = await createMintInterface(rpc, payer, payer, null, 9);
37+
await mintToCompressed(rpc, payer, mint, payer, [{ recipient: payer.publicKey, amount: 1000n }]);
38+
39+
const recipient = Keypair.generate();
40+
await createAtaInterface(rpc, payer, mint, recipient.publicKey);
41+
42+
const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey);
43+
const recipientAta = getAssociatedTokenAddressInterface(
44+
mint,
45+
recipient.publicKey,
46+
);
47+
48+
// Warm up: load compressed tokens (cold) to sender's hot balance
49+
// Returns [] if already hot — safe to call unconditionally
50+
const loadIxs = await createLoadAtaInstructions(
51+
rpc,
52+
senderAta,
53+
payer.publicKey,
54+
mint,
55+
payer.publicKey,
56+
);
57+
58+
if (loadIxs.length > 0) {
59+
const blockhash = await rpc.getLatestBlockhash();
60+
const loadTx = buildAndSignTx(loadIxs, payer, blockhash.blockhash);
61+
await sendAndConfirmTx(rpc, loadTx);
62+
}
63+
64+
// Trade: transfer from hot balance
65+
const transferIx = createTransferInterfaceInstruction(
66+
senderAta,
67+
recipientAta,
68+
payer.publicKey,
69+
500n,
70+
);
71+
72+
const blockhash = await rpc.getLatestBlockhash();
73+
const tradeTx = buildAndSignTx([transferIx], payer, blockhash.blockhash);
74+
const signature = await sendAndConfirmTx(rpc, tradeTx);
75+
76+
console.log("Tx:", signature);
77+
})();```

0 commit comments

Comments
 (0)