Skip to content

Commit e81c9b4

Browse files
refactor: AccountInterface: use photon v2 types and simplify ColdContext (#2274)
* feat: integrate photon submodule versioning into build process * Refactor AccountInterface handling and remove unused functions - Removed `make_get_token_account_interface_body` and `make_get_ata_interface_body` functions from the photon API module. - Updated various test files to remove references to `ColdContext` and directly use compressed accounts in `AccountInterface`. - Adjusted `AccountSpec::Ata` instantiation to wrap `ata_interface` in `Box::new`. - Cleaned up imports in multiple test files by removing unused `ColdContext` references. * format
1 parent 221c1aa commit e81c9b4

32 files changed

Lines changed: 11811 additions & 17540 deletions

‎cli/.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ test-ledger
1515
/bin
1616
/config.json
1717
/src/utils/proverVersion.generated.ts
18+
/src/utils/photonVersion.generated.ts

‎cli/package.json‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,9 @@
9393
"scripts": {
9494
"postinstall": "[ -d ./bin ] && find ./bin -type f -exec chmod +x {} + || echo 'No bin directory found, skipping chmod'",
9595
"sync-prover-version": "./scripts/syncProverVersion.sh",
96-
"build": "shx rm -rf dist && pnpm sync-prover-version && pnpm tsc -p tsconfig.json && pnpm tsc -p tsconfig.test.json",
97-
"build-release": "shx rm -rf dist && pnpm sync-prover-version && pnpm tsc -p tsconfig.json && pnpm tsc -p tsconfig.test.json",
96+
"sync-photon-version": "./scripts/syncPhotonVersion.sh",
97+
"build": "shx rm -rf dist && pnpm sync-prover-version && pnpm sync-photon-version && pnpm tsc -p tsconfig.json && pnpm tsc -p tsconfig.test.json",
98+
"build-release": "shx rm -rf dist && pnpm sync-prover-version && pnpm sync-photon-version && pnpm tsc -p tsconfig.json && pnpm tsc -p tsconfig.test.json",
9899
"format": "pnpm prettier --write \"src/**/*.{ts,js}\" \"test/**/*.{ts,js}\" -w",
99100
"format:check": "pnpm prettier \"src/**/*{ts,js}\" \"test/**/*.{ts,js}\" --check",
100101
"lint": "eslint .",

‎cli/scripts/syncPhotonVersion.sh‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/bin/bash
2+
# Syncs the photon version and commit from the external/photon submodule to a TypeScript constant.
3+
# This script is run as part of the CLI build process.
4+
5+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6+
CLI_DIR="$(dirname "$SCRIPT_DIR")"
7+
REPO_ROOT="$(dirname "$CLI_DIR")"
8+
9+
PHOTON_DIR="$REPO_ROOT/external/photon"
10+
OUTPUT_FILE="$CLI_DIR/src/utils/photonVersion.generated.ts"
11+
12+
if [ ! -d "$PHOTON_DIR" ]; then
13+
echo "Error: photon submodule not found at $PHOTON_DIR"
14+
echo " Run: git submodule update --init external/photon"
15+
exit 1
16+
fi
17+
18+
VERSION=$(grep '^version' "$PHOTON_DIR/Cargo.toml" | head -1 | sed 's/.*"\(.*\)".*/\1/')
19+
COMMIT=$(git -C "$PHOTON_DIR" rev-parse HEAD 2>/dev/null)
20+
21+
if [ -z "$VERSION" ] || [ -z "$COMMIT" ]; then
22+
echo "Error: Could not extract version or commit from photon submodule"
23+
exit 1
24+
fi
25+
26+
REPO="https://github.com/lightprotocol/photon.git"
27+
28+
cat > "$OUTPUT_FILE" << EOF
29+
// Auto-generated from external/photon submodule - do not edit manually
30+
export const PHOTON_VERSION = "$VERSION";
31+
export const PHOTON_GIT_COMMIT = "$COMMIT";
32+
export const PHOTON_GIT_REPO = "$REPO";
33+
EOF
34+
35+
echo "Synced photon version $VERSION (commit $COMMIT) to $OUTPUT_FILE"

‎cli/src/utils/constants.ts‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,8 @@ export const FORESTER_PROCESS_NAME = "forester";
2424
export const SURFPOOL_VERSION = "1.0.1";
2525
export const SURFPOOL_RELEASE_TAG = "v1.0.1-light";
2626

27-
export const PHOTON_VERSION = "0.51.2";
28-
29-
// Set these to override Photon requirements with a specific git commit:
30-
export const USE_PHOTON_FROM_GIT = true; // If true, will show git install command instead of crates.io.
31-
export const PHOTON_GIT_REPO = "https://github.com/lightprotocol/photon.git";
32-
export const PHOTON_GIT_COMMIT = "32e9ae60926a0d614ad444b799d15c15c02f2ef7"; // If empty, will use main branch.
27+
// PHOTON_VERSION, PHOTON_GIT_COMMIT, and PHOTON_GIT_REPO are auto-generated
28+
// from the external/photon submodule at build time. See photonVersion.generated.ts.
3329
export const LIGHT_PROTOCOL_PROGRAMS_DIR_ENV = "LIGHT_PROTOCOL_PROGRAMS_DIR";
3430
export const BASE_PATH = "../../bin/";
3531

‎cli/src/utils/processPhotonIndexer.ts‎

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import which from "which";
22
import { killProcess, spawnBinary, waitForServers } from "./process";
3+
import { INDEXER_PROCESS_NAME } from "./constants";
34
import {
4-
INDEXER_PROCESS_NAME,
55
PHOTON_VERSION,
6-
USE_PHOTON_FROM_GIT,
76
PHOTON_GIT_REPO,
87
PHOTON_GIT_COMMIT,
9-
} from "./constants";
8+
} from "./photonVersion.generated";
109
import { exec } from "node:child_process";
1110
import * as util from "node:util";
1211
import { exit } from "node:process";
@@ -27,13 +26,7 @@ async function isExpectedPhotonVersion(
2726
}
2827

2928
function getPhotonInstallMessage(): string {
30-
if (USE_PHOTON_FROM_GIT && PHOTON_GIT_COMMIT) {
31-
return `\nPhoton indexer ${PHOTON_VERSION} (commit ${PHOTON_GIT_COMMIT}) not found. Please install it by running: "cargo install --git ${PHOTON_GIT_REPO} --rev ${PHOTON_GIT_COMMIT} --locked --force"`;
32-
} else if (USE_PHOTON_FROM_GIT) {
33-
return `\nPhoton indexer ${PHOTON_VERSION} not found. Please install it by running: "cargo install --git ${PHOTON_GIT_REPO} --locked --force"`;
34-
} else {
35-
return `\nPhoton indexer ${PHOTON_VERSION} not found. Please install it by running: "cargo install photon-indexer --version ${PHOTON_VERSION} --locked --force"`;
36-
}
29+
return `\nPhoton indexer ${PHOTON_VERSION} (commit ${PHOTON_GIT_COMMIT}) not found. Please install it by running: "cargo install --git ${PHOTON_GIT_REPO} --rev ${PHOTON_GIT_COMMIT} --locked --force"`;
3730
}
3831

3932
export async function startIndexer(

‎scripts/devenv/install-photon.sh‎

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,23 @@ sed_inplace() {
1313
}
1414

1515
install_photon() {
16-
local expected_version="${PHOTON_VERSION}"
17-
local expected_commit="${PHOTON_COMMIT}"
16+
local photon_path="${REPO_ROOT}/external/photon"
17+
18+
# Ensure photon submodule is initialized and up to date
19+
echo "Updating photon submodule..."
20+
cd "${REPO_ROOT}"
21+
git submodule update --init --recursive external/photon
22+
cd "${SCRIPT_DIR}"
23+
24+
# Derive version and commit from the actual submodule state (after init)
25+
local expected_version
26+
expected_version=$(grep '^version' "${photon_path}/Cargo.toml" | head -1 | sed 's/.*"\(.*\)".*/\1/')
27+
local expected_commit
28+
expected_commit=$(git -C "${photon_path}" rev-parse HEAD)
1829
local install_marker="photon:${expected_version}:${expected_commit}"
1930

20-
# Validate required variables
2131
if [ -z "${expected_version}" ] || [ -z "${expected_commit}" ]; then
22-
echo "ERROR: PHOTON_VERSION or PHOTON_COMMIT not set in versions.sh"
32+
echo "ERROR: Could not derive version or commit from external/photon submodule."
2333
exit 1
2434
fi
2535

@@ -45,8 +55,8 @@ install_photon() {
4555
sed_inplace "/^photon:/d" "$INSTALL_LOG" 2>/dev/null || true
4656
sed_inplace "/^photon$/d" "$INSTALL_LOG" 2>/dev/null || true
4757

48-
echo "Installing Photon indexer ${expected_version} (commit ${expected_commit})..."
49-
RUSTFLAGS="-A dead-code" cargo install --git https://github.com/helius-labs/photon.git --rev ${expected_commit} --locked --force
58+
echo "Installing Photon indexer ${expected_version} (commit ${expected_commit}) from submodule..."
59+
RUSTFLAGS="-A dead-code" cargo install --path "${photon_path}" --locked --force
5060

5161
# Verify installation succeeded
5262
if [ ! -f "${PREFIX}/cargo/bin/photon" ]; then

‎scripts/devenv/versions.sh‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "${PWD}")"
66
export RUST_VERSION=$(grep 'channel' "${REPO_ROOT}/rust-toolchain.toml" | sed 's/.*"\(.*\)".*/\1/' | cut -d'.' -f1,2)
77
export GO_VERSION=$(grep '^go ' "${REPO_ROOT}/prover/server/go.mod" | awk '{print $2}')
88
export PNPM_VERSION=$(grep 'packageManager' "${REPO_ROOT}/package.json" | sed 's/.*pnpm@\([^"]*\).*/\1/')
9+
export PHOTON_COMMIT=$(git -C "${REPO_ROOT}" rev-parse HEAD:external/photon 2>/dev/null || echo "unknown")
10+
export PHOTON_VERSION=$(grep '^version' "${REPO_ROOT}/external/photon/Cargo.toml" 2>/dev/null | head -1 | sed 's/.*"\(.*\)".*/\1/')
911

1012
# Versions to bump manually (edit below)
1113
export NODE_VERSION="22.16.0"
1214
export SOLANA_VERSION="2.2.15"
1315
export ANCHOR_VERSION="0.31.1"
1416
export JQ_VERSION="1.8.0"
15-
export PHOTON_VERSION="0.51.2"
16-
export PHOTON_COMMIT="301153a04c3232413198098a0a6725207ce36298"
1717
export REDIS_VERSION="8.0.1"
1818

1919
export ANCHOR_TAG="anchor-v${ANCHOR_VERSION}"

‎sdk-libs/client/src/indexer/photon_indexer.rs‎

Lines changed: 38 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,11 +1716,8 @@ impl Indexer for PhotonIndexer {
17161716
}
17171717

17181718
// ============ Interface Methods ============
1719-
// These methods use the Interface endpoints that race hot (on-chain) and cold (compressed) lookups
1720-
1719+
// These methods use the Interface endpoints that race hot (on-chain) and cold (compressed) lookups.
17211720
impl PhotonIndexer {
1722-
/// Get account data from either on-chain or compressed sources.
1723-
/// Races both lookups and returns the result with the higher slot.
17241721
pub async fn get_account_interface(
17251722
&self,
17261723
address: &Pubkey,
@@ -1764,99 +1761,46 @@ impl PhotonIndexer {
17641761
.await
17651762
}
17661763

1767-
/// Get token account data from either on-chain or compressed sources.
1768-
/// Races both lookups and returns the result with the higher slot.
17691764
pub async fn get_token_account_interface(
17701765
&self,
17711766
address: &Pubkey,
17721767
config: Option<IndexerRpcConfig>,
17731768
) -> Result<Response<Option<TokenAccountInterface>>, IndexerError> {
1774-
let config = config.unwrap_or_default();
1775-
self.retry(config.retry_config, || async {
1776-
let params = photon_api::types::PostGetTokenAccountInterfaceBodyParams {
1777-
address: photon_api::types::SerializablePubkey(address.to_string()),
1778-
};
1779-
let request =
1780-
photon_api::apis::default_api::make_get_token_account_interface_body(params);
1781-
1782-
let result = photon_api::apis::default_api::get_token_account_interface_post(
1783-
&self.configuration,
1784-
request,
1785-
)
1786-
.await?;
1787-
1788-
let api_response = Self::extract_result_with_error_check(
1789-
"get_token_account_interface",
1790-
result.error,
1791-
result.result,
1792-
)?;
1793-
1794-
if api_response.context.slot < config.slot {
1795-
return Err(IndexerError::IndexerNotSyncedToSlot);
1769+
let response = self.get_account_interface(address, config).await?;
1770+
let value = match response.value {
1771+
Some(ai) => {
1772+
let token = parse_token_data_from_indexer_account(&ai)?;
1773+
Some(TokenAccountInterface { account: ai, token })
17961774
}
1797-
1798-
let account = match api_response.value {
1799-
Some(ref tai) => Some(TokenAccountInterface::try_from(tai)?),
1800-
None => None,
1801-
};
1802-
1803-
Ok(Response {
1804-
context: Context {
1805-
slot: api_response.context.slot,
1806-
},
1807-
value: account,
1808-
})
1775+
None => None,
1776+
};
1777+
Ok(Response {
1778+
context: response.context,
1779+
value,
18091780
})
1810-
.await
18111781
}
18121782

1813-
/// Get Associated Token Account data from either on-chain or compressed sources.
1814-
/// Derives the Light Protocol ATA address from owner+mint, then races hot/cold lookups.
18151783
pub async fn get_associated_token_account_interface(
18161784
&self,
18171785
owner: &Pubkey,
18181786
mint: &Pubkey,
18191787
config: Option<IndexerRpcConfig>,
18201788
) -> Result<Response<Option<TokenAccountInterface>>, IndexerError> {
1821-
let config = config.unwrap_or_default();
1822-
self.retry(config.retry_config, || async {
1823-
let params = photon_api::types::PostGetAtaInterfaceBodyParams {
1824-
owner: photon_api::types::SerializablePubkey(owner.to_string()),
1825-
mint: photon_api::types::SerializablePubkey(mint.to_string()),
1826-
};
1827-
let request = photon_api::apis::default_api::make_get_ata_interface_body(params);
1828-
1829-
let result =
1830-
photon_api::apis::default_api::get_ata_interface_post(&self.configuration, request)
1831-
.await?;
1832-
1833-
let api_response = Self::extract_result_with_error_check(
1834-
"get_associated_token_account_interface",
1835-
result.error,
1836-
result.result,
1837-
)?;
1838-
1839-
if api_response.context.slot < config.slot {
1840-
return Err(IndexerError::IndexerNotSyncedToSlot);
1789+
let ata_address = light_token::instruction::get_associated_token_address(owner, mint);
1790+
let response = self.get_account_interface(&ata_address, config).await?;
1791+
let value = match response.value {
1792+
Some(ai) => {
1793+
let token = parse_token_data_from_indexer_account(&ai)?;
1794+
Some(TokenAccountInterface { account: ai, token })
18411795
}
1842-
1843-
let account = match api_response.value {
1844-
Some(ref tai) => Some(TokenAccountInterface::try_from(tai)?),
1845-
None => None,
1846-
};
1847-
1848-
Ok(Response {
1849-
context: Context {
1850-
slot: api_response.context.slot,
1851-
},
1852-
value: account,
1853-
})
1796+
None => None,
1797+
};
1798+
Ok(Response {
1799+
context: response.context,
1800+
value,
18541801
})
1855-
.await
18561802
}
18571803

1858-
/// Get multiple account interfaces in a batch.
1859-
/// Returns a vector where each element corresponds to an input address.
18601804
pub async fn get_multiple_account_interfaces(
18611805
&self,
18621806
addresses: Vec<&Pubkey>,
@@ -1909,3 +1853,19 @@ impl PhotonIndexer {
19091853
.await
19101854
}
19111855
}
1856+
1857+
/// Parse token data from an indexer AccountInterface.
1858+
/// For compressed (cold) accounts: borsh-deserializes TokenData from the cold data bytes.
1859+
/// For on-chain (hot) accounts: returns default TokenData (downstream conversion re-parses from SPL layout).
1860+
fn parse_token_data_from_indexer_account(
1861+
ai: &AccountInterface,
1862+
) -> Result<light_token::compat::TokenData, IndexerError> {
1863+
match &ai.cold {
1864+
Some(cold) => borsh::BorshDeserialize::deserialize(&mut cold.data.data.as_slice())
1865+
.map_err(|e| IndexerError::decode_error("token_data", e)),
1866+
None => {
1867+
// Hot account — downstream will re-parse from SPL account data directly
1868+
Ok(light_token::compat::TokenData::default())
1869+
}
1870+
}
1871+
}

0 commit comments

Comments
 (0)