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
7 changes: 7 additions & 0 deletions .github/workflows/js-v2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ jobs:
source ./scripts/devenv.sh
npx nx test @lightprotocol/compressed-token

- name: Run sdk-anchor-test TypeScript tests with V2
run: |
source ./scripts/devenv.sh
npx nx build @lightprotocol/sdk-anchor-test
cd sdk-tests/sdk-anchor-test
npm run test-ts

- name: Display prover logs on failure
if: failure()
run: |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

# Rust
**/target/
!sdk-tests/sdk-anchor-test/target/

**/dist/

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ pinocchio-pubkey = { version = "0.3.0" }
bs58 = "^0.5.1"
litesvm = "0.7"
# Anchor
anchor-lang = { version = "=0.31.1", features = ["idl-build"] }
anchor-lang = { version = "=0.31.1" }
anchor-spl = "=0.31.1"

# Anchor compatibility
Expand Down
2 changes: 1 addition & 1 deletion anchor-programs/system/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ name = "light_system_program"
[features]
no-entrypoint = []
cpi = ["no-entrypoint"]
default = ["idl-build"]
default = []
idl-build = ["anchor-lang/idl-build"]
test-sbf = []

Expand Down
2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"/oclif.manifest.json"
],
"dependencies": {
"@coral-xyz/anchor": "0.29.0",
"@coral-xyz/anchor": "^0.29.0",
"@lightprotocol/compressed-token": "workspace:*",
"@lightprotocol/hasher.rs": "0.2.1",
"@lightprotocol/stateless.js": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion js/stateless.js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"superstruct": "2.0.2"
},
"devDependencies": {
"@coral-xyz/anchor": "0.29.0",
"@coral-xyz/anchor": "^0.29.0",
"@coral-xyz/borsh": "^0.29.0",
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
"@eslint/js": "9.36.0",
Expand Down
16 changes: 15 additions & 1 deletion js/stateless.js/src/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ import {
ValidityProof,
TreeType,
AddressTreeInfo,
CompressedAccount,
} from './state';
import { array, create, nullable } from 'superstruct';
import {
Expand All @@ -74,6 +73,7 @@ import {
defaultStateTreeLookupTables,
versionedEndpoint,
featureFlags,
batchAddressTree,
} from './constants';
import BN from 'bn.js';
import { toCamelCase, toHex } from './utils/conversion';
Expand Down Expand Up @@ -702,6 +702,20 @@ export class Rpc extends Connection implements CompressionApiInterface {
}
}

/**
* Get a V2 address tree info.
*/
async getAddressTreeInfoV2(): Promise<TreeInfo> {
const tree = new PublicKey(batchAddressTree);
return {
tree,
queue: tree,
cpiContext: undefined,
treeType: TreeType.AddressV2,
nextTreeInfo: null,
};
}

/**
* Fetch the compressed account for the specified account address or hash
*/
Expand Down
15 changes: 15 additions & 0 deletions js/stateless.js/src/test-helpers/test-rpc/test-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { getParsedEvents } from './get-parsed-events';
import {
defaultTestStateTreeAccounts,
localTestActiveStateTreeInfos,
batchAddressTree,
} from '../../constants';
import {
AddressWithTree,
Expand Down Expand Up @@ -179,6 +180,20 @@ export class TestRpc extends Connection implements CompressionApiInterface {
throw new Error('doFetch not supported in test-rpc');
}

/**
* Get a V2 address tree info.
*/
async getAddressTreeInfoV2(): Promise<TreeInfo> {
const tree = new PublicKey(batchAddressTree);
return {
tree,
queue: tree,
cpiContext: undefined,
treeType: TreeType.AddressV2,
nextTreeInfo: null,
};
}

/**
* Fetch the compressed account for the specified account hash
*/
Expand Down
1 change: 1 addition & 0 deletions js/stateless.js/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from './airdrop';
export * from './calculate-compute-unit-price';
export * from './conversion';
export * from './dedupe-signer';
export * from './instruction';
export * from './parse-validity-proof';
export * from './pipe';
export * from './send-and-confirm';
Expand Down
253 changes: 253 additions & 0 deletions js/stateless.js/src/utils/instruction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
import { AccountMeta, PublicKey, SystemProgram } from '@solana/web3.js';
import { defaultStaticAccountsStruct } from '../constants';
import { LightSystemProgram } from '../programs';

export class PackedAccounts {
private preAccounts: AccountMeta[] = [];
private systemAccounts: AccountMeta[] = [];
private nextIndex: number = 0;
private map: Map<PublicKey, [number, AccountMeta]> = new Map();

static newWithSystemAccounts(
config: SystemAccountMetaConfig,
): PackedAccounts {
const instance = new PackedAccounts();
instance.addSystemAccounts(config);
return instance;
}

static newWithSystemAccountsV2(
config: SystemAccountMetaConfig,
): PackedAccounts {
const instance = new PackedAccounts();
instance.addSystemAccountsV2(config);
return instance;
}

addPreAccountsSigner(pubkey: PublicKey): void {
this.preAccounts.push({ pubkey, isSigner: true, isWritable: false });
}

addPreAccountsSignerMut(pubkey: PublicKey): void {
this.preAccounts.push({ pubkey, isSigner: true, isWritable: true });
}

addPreAccountsMeta(accountMeta: AccountMeta): void {
this.preAccounts.push(accountMeta);
}

addSystemAccounts(config: SystemAccountMetaConfig): void {
this.systemAccounts.push(...getLightSystemAccountMetas(config));
}

addSystemAccountsV2(config: SystemAccountMetaConfig): void {
this.systemAccounts.push(...getLightSystemAccountMetasV2(config));
}

insertOrGet(pubkey: PublicKey): number {
return this.insertOrGetConfig(pubkey, false, true);
}

insertOrGetReadOnly(pubkey: PublicKey): number {
return this.insertOrGetConfig(pubkey, false, false);
}

insertOrGetConfig(
pubkey: PublicKey,
isSigner: boolean,
isWritable: boolean,
): number {
const entry = this.map.get(pubkey);
if (entry) {
return entry[0];
}
const index = this.nextIndex++;
const meta: AccountMeta = { pubkey, isSigner, isWritable };
this.map.set(pubkey, [index, meta]);
return index;
}

private hashSetAccountsToMetas(): AccountMeta[] {
const entries = Array.from(this.map.entries());
entries.sort((a, b) => a[1][0] - b[1][0]);
return entries.map(([, [, meta]]) => meta);
}

private getOffsets(): [number, number] {
const systemStart = this.preAccounts.length;
const packedStart = systemStart + this.systemAccounts.length;
return [systemStart, packedStart];
}

toAccountMetas(): {
remainingAccounts: AccountMeta[];
systemStart: number;
packedStart: number;
} {
const packed = this.hashSetAccountsToMetas();
const [systemStart, packedStart] = this.getOffsets();
return {
remainingAccounts: [
...this.preAccounts,
...this.systemAccounts,
...packed,
],
systemStart,
packedStart,
};
}
}

export class SystemAccountMetaConfig {
selfProgram: PublicKey;
cpiContext?: PublicKey;
solCompressionRecipient?: PublicKey;
solPoolPda?: PublicKey;

private constructor(
selfProgram: PublicKey,
cpiContext?: PublicKey,
solCompressionRecipient?: PublicKey,
solPoolPda?: PublicKey,
) {
this.selfProgram = selfProgram;
this.cpiContext = cpiContext;
this.solCompressionRecipient = solCompressionRecipient;
this.solPoolPda = solPoolPda;
}

static new(selfProgram: PublicKey): SystemAccountMetaConfig {
return new SystemAccountMetaConfig(selfProgram);
}

static newWithCpiContext(
selfProgram: PublicKey,
cpiContext: PublicKey,
): SystemAccountMetaConfig {
return new SystemAccountMetaConfig(selfProgram, cpiContext);
}
}

export function getLightSystemAccountMetas(
config: SystemAccountMetaConfig,
): AccountMeta[] {
let signerSeed = new TextEncoder().encode('cpi_authority');
const cpiSigner = PublicKey.findProgramAddressSync(
[signerSeed],
config.selfProgram,
)[0];
const defaults = defaultStaticAccountsStruct();
const metas: AccountMeta[] = [
{
pubkey: LightSystemProgram.programId,
isSigner: false,
isWritable: false,
},
{ pubkey: cpiSigner, isSigner: false, isWritable: false },
{
pubkey: defaults.registeredProgramPda,
isSigner: false,
isWritable: false,
},
{ pubkey: defaults.noopProgram, isSigner: false, isWritable: false },
{
pubkey: defaults.accountCompressionAuthority,
isSigner: false,
isWritable: false,
},
{
pubkey: defaults.accountCompressionProgram,
isSigner: false,
isWritable: false,
},
{ pubkey: config.selfProgram, isSigner: false, isWritable: false },
];
if (config.solPoolPda) {
metas.push({
pubkey: config.solPoolPda,
isSigner: false,
isWritable: true,
});
}
if (config.solCompressionRecipient) {
metas.push({
pubkey: config.solCompressionRecipient,
isSigner: false,
isWritable: true,
});
}
metas.push({
pubkey: SystemProgram.programId,
isSigner: false,
isWritable: false,
});
if (config.cpiContext) {
metas.push({
pubkey: config.cpiContext,
isSigner: false,
isWritable: true,
});
}
return metas;
}

export function getLightSystemAccountMetasV2(
config: SystemAccountMetaConfig,
): AccountMeta[] {
let signerSeed = new TextEncoder().encode('cpi_authority');
const cpiSigner = PublicKey.findProgramAddressSync(
[signerSeed],
config.selfProgram,
)[0];
const defaults = defaultStaticAccountsStruct();
const metas: AccountMeta[] = [
{
pubkey: LightSystemProgram.programId,
isSigner: false,
isWritable: false,
},
{ pubkey: cpiSigner, isSigner: false, isWritable: false },
{
pubkey: defaults.registeredProgramPda,
isSigner: false,
isWritable: false,
},
{
pubkey: defaults.accountCompressionAuthority,
isSigner: false,
isWritable: false,
},
{
pubkey: defaults.accountCompressionProgram,
isSigner: false,
isWritable: false,
},
{
pubkey: SystemProgram.programId,
isSigner: false,
isWritable: false,
},
];
if (config.solPoolPda) {
metas.push({
pubkey: config.solPoolPda,
isSigner: false,
isWritable: true,
});
}
if (config.solCompressionRecipient) {
metas.push({
pubkey: config.solCompressionRecipient,
isSigner: false,
isWritable: true,
});
}
if (config.cpiContext) {
metas.push({
pubkey: config.cpiContext,
isSigner: false,
isWritable: true,
});
}
return metas;
}
Loading
Loading