Skip to main content
Loading method details…

Constructing Calls

The getLedgerEntries method returns the "values" (or "entries") for a given set of "keys." Ledger keys come in a lot of forms, and we'll go over the commonly used ones on this page alongside tutorials on how to build and use them.

The source of truth should always be the XDR defined in the protocol. LedgerKeys are a union type defined in Stellar-ledger-entries.x.

An interesting product of the store's internal design is that the key is a subset of the entry: we'll see more of this later.

For more on the types of LedgerKey entries and their purposes, see Ledger Entries.

Accounts

To fetch an account, all you need is its public key:

import { Keypair, xdr } from "@stellar/stellar-sdk";

const publicKey = "GALAXYVOIDAOPZTDLHILAJQKCVVFMD4IKLXLSZV5YHO7VY74IWZILUTO";
const accountLedgerKey = xdr.LedgerKey.ledgerKeyAccount(
new xdr.LedgerKeyAccount({
accountId: Keypair.fromPublicKey(publicKey).xdrAccountId(),
}),
);
console.log(accountLedgerKey.toXDR("base64"));

This will give you the full account details.

const accountEntryData = (
await s.getLedgerEntries(accountLedgerKey)
).entries[0].account();

If you just want to take a look at the structure, you can pass the raw base64 value we logged above to the Laboratory (or via curl if you pass "xdrFormat": "json" as an additional parameter to getLedgerEntries) and see all of the possible fields. You can also dig into them in code, of course:

console.log(
`Account ${publicKey} has ${accountEntryData
.balance()
.toString()} stroops of XLM and is on sequence number ${accountEntryData
.seqNum()
.toString()}`,
);

Trustlines

A trustline is a balance entry for any non-native asset like AstroDollars. To fetch one, you need the trustline owner (a public key like for Accounts) and the asset in question:

const trustlineLedgerKey = xdr.LedgerKey.ledgerKeyTrustLine(
new xdr.LedgerKeyTrustLine({
accountId: Keypair.fromPublicKey(publicKey).xdrAccountId(),
asset: new Asset(
"AstroDollar",
"GDRM3MK6KMHSYIT4E2AG2S2LWTDBJNYXE4H72C7YTTRWOWX5ZBECFWO7",
).toTrustLineXDRObject(),
}),
);

Much like an account, the resulting entry has a balance, but it also has a limit and flags to control how much of that asset can be held. The asset, however, can be either an issued asset or a liquidity pool:

let asset: string;
let rawAsset = trustlineEntryData.asset();

switch (rawAsset.switch().value) {
case AssetType.assetTypeCreditAlphanum4().value:
asset = Asset.fromOperation(
xdr.Asset.assetTypeCreditAlphanum4(rawAsset.alphaNum4()),
).toString();
break;

case AssetType.assetTypeCreditAlphanum12().value:
asset = Asset.fromOperation(
xdr.Asset.assetTypeCreditAlphanum12(rawAsset.alphaNum12()),
).toString();
break;

case AssetType.assetTypePoolShare().value:
asset = rawAsset.liquidityPoolId().toXDR("hex");
break;
}

console.log(
`Account ${publicKey} has ${trustlineEntryData
.balance()
.toString()} stroops of ${asset} with a limit of ${trustlineEntryData
.limit()
.toString()}`,
);

Offers

An offer represents a live order on the DEX. Each offer is identified by its offerID. To construct a LedgerKey for an offer, you only need the offer ID:

const offerLedgerKey = xdr.LedgerKey.ledgerKeyOffer(
new xdr.LedgerKeyOffer({
offerID: 123456789n,
}),
);
console.log(offerLedgerKey.toXDR("base64"));

Once you have the ledger entry, you can extract its fields:

const offerEntryData = (
await s.getLedgerEntries(offerLedgerKey)
).entries[0].offer();

console.log(`Offer ID: ${offerEntryData.offerId().toString()}`);
console.log(`Seller: ${offerEntryData.sellerId().accountId()}`);
console.log(`Amount: ${offerEntryData.amount().toString()}`); // of selling
console.log(
`Price: ${offerEntryData.price().n().toString()} / ${offerEntryData.price().d().toString()}`,
);

You can then decode the selling and buying assets the same way you do for Trustlines, by inspecting offerEntryData.selling() and offerEntryData.buying() using the standard xdr.Asset parsing logic.

Claimable Balances

A claimable balance represents assets that have been locked under certain claim conditions. Each claimable balance is identified by its balanceId, which is a 32-byte hash.

To construct a LedgerKey for a claimable balance, you need the balanceId as a Buffer or Uint8Array:

const balanceIdHex =
"407a334017a508fb2bf41952c74f977b46147ed70b175717f8bacc0ca3f2cc5b";
const balanceIdBytes = Buffer.from(balanceIdHex, "hex");

const claimableBalanceLedgerKey = xdr.LedgerKey.ledgerKeyClaimableBalance(
new xdr.LedgerKeyClaimableBalance({
balanceID: xdr.ClaimableBalanceID.claimableBalanceIdTypeV0(balanceIdBytes),
}),
);
console.log(claimableBalanceLedgerKey.toXDR("base64"));

Once you have the ledger entry, you can extract its fields:

const claimableBalanceEntryData = (
await s.getLedgerEntries(claimableBalanceLedgerKey)
).entries[0].claimableBalance();

console.log(`Amount: ${claimableBalanceEntryData.amount().toString()} stroops`);

const asset = claimableBalanceEntryData.asset();
// Decode asset similar to as in trustlines

const claimants = claimableBalanceEntryData.claimants();

if (claimableBalanceEntryData.ext().switch().value === 1) {
const flags = claimableBalanceEntryData.ext().v1().flags();
const clawbackEnabled =
(flags &
xdr.ClaimableBalanceFlags.claimableBalanceClawbackEnabledFlag()) !==
0;
console.log(`Clawback Enabled: ${clawbackEnabled}`);
}

Claimants and Predicates

Claimable balances contain one or more claimants, each with a claim predicate. You can iterate through them as follows:

for (let i = 0; i < claimants.length; i++) {
const claimant = claimants[i].v0();
const destination = claimant.destination().accountId();
console.log(`Claimant ${i + 1}: ${destination}`);

const predicate = claimant.predicate();
const predicateType = predicate.switch().value;

switch (predicateType) {
case xdr.ClaimPredicateType.claimPredicateUnconditional().value:
console.log("Predicate: Unconditional");
break;
case xdr.ClaimPredicateType.claimPredicateBeforeAbsoluteTime().value:
console.log(`Predicate: Before Absolute Time = ${predicate.absBefore().toString()}`);
break;
case xdr.ClaimPredicateType.claimPredicateBeforeRelativeTime().value:
console.log(`Predicate: Before Relative Time = ${predicate.relBefore().toString()} seconds`);
break;
default:
console.log("Predicate: Complex predicate type (AND/OR/NOT)");
break;
}

Flags

If the claimable balance has flags set (not v0), you can read them like this:

switch (claimableBalanceEntryData.ext().switch().value) {
case 0: // No flags
break;
case 1:
const flags = claimableBalanceEntryData.ext().v1().flags();
const clawbackEnabled = (flags & 1) !== 0; // claw = 0x1
console.log(`Clawback Enabled: ${clawbackEnabled}`);
break;
}

Liquidity Pools

A liquidity pool represents an Automated Market Maker holding two assets. Each liquidity pool is identified by its liquidityPoolID, which is a 32-byte hash.

To construct a LedgerKey for an AMM, you need the liquidity-pool ID as a Buffer or Uint8Array. Core deterministicly stores this as a 64-char hex string.

const liquidityPoolIdHex =
"82f857462d5304e1ad7d5308fb6d90ff3e70ad8fb07b81d04b12d2cc867fc735";
const liquidityPoolIdBytes = Buffer.from(liquidityPoolIdHex, "hex");

const liquidityPoolLedgerKey = xdr.LedgerKey.ledgerKeyLiquidityPool(
new xdr.LedgerKeyLiquidityPool({
liquidityPoolID: liquidityPoolIdBytes,
}),
);
console.log(liquidityPoolLedgerKey.toXDR("base64"));

Once you have the ledger entry, you can extract its fields:

const liquidityPoolEntryData = (
await s.getLedgerEntries(liquidityPoolLedgerKey)
).entries[0].liquidityPool();

console.log(
`Total Pool Shares: ${liquidityPoolEntryData.totalPoolShares().toString()}`,
);
console.log(
`Total Trustlines: ${liquidityPoolEntryData.poolSharesTrustLineCount().toString()}`,
);

const AMM = liquidityPoolEntryData.body();
switch (AMM.switch().value) {
case xdr.LiquidityPoolType.liquidityPoolConstantProduct().value: {
const params = AMM.constantProduct().params();
const fee = params.fee(); // basis points
const assetA = params.assetA();
const assetB = params.assetB();
console.log(`Constant-Product Between: ${assetA} and ${assetB}`);
break;
}
}

Contract Data

Suppose we've deployed the increment example contract and want to find out what value is stored in the COUNTER ledger key. To build the key:

import { xdr, Address } from "@stellar/stellar-sdk";

const getLedgerKeySymbol = (
contractId: string,
symbolText: string,
): xdr.LedgerKey => {
return xdr.LedgerKey.contractData(
new xdr.LedgerKeyContractData({
contract: new Address(contractId).toScAddress(),
key: xdr.ScVal.scvSymbol(symbolText),
// The increment contract stores its state in persistent storage,
// but other contracts may use temporary storage
// (xdr.ContractDataDurability.temporary()).
durability: xdr.ContractDataDurability.persistent(),
}),
);
};

const ledgerKey = getLedgerKeySymbol(
"CCPYZFKEAXHHS5VVW5J45TOU7S2EODJ7TZNJIA5LKDVL3PESCES6FNCI",
"COUNTER",
);

Contract Wasm Code

To understand this, we need a handle on how smart contract deployment works:

  • When you deploy a contract, first the code is "installed" (i.e. uploaded onto the blockchain), creating a LedgerEntry with the Wasm byte-code that can be uniquely identified by its hash (that is, the hash of the uploaded code itself).
  • Then, when a contract instance is "instantiated," we create a LedgerEntry with a reference to that code's hash. This means many contracts can point to the same Wasm code.

Thus, fetching the contract code is a two-step process:

  1. First, we look up the contract itself, to see which code hash it is referencing.
  2. Then, we can look up the raw Wasm byte-code using that hash.

1. Find the ledger key for the contract instance

import { Contract } from "@stellar/stellar-sdk";

function getLedgerKeyContractCode(contractId): xdr.LedgerKey {
return new Contract(contractId).getFootprint();
}

console.log(
getLedgerKeyContractCode(
"CCPYZFKEAXHHS5VVW5J45TOU7S2EODJ7TZNJIA5LKDVL3PESCES6FNCI",
),
);

Once we have the ledger entry (via getLedgerEntries, see below), we can extract the Wasm hash:

2. Request the ContractCode using the retrieved LedgerKey

Now take the xdr field from the previous response's result object, and create a LedgerKey from the hash contained inside.

import { xdr } from "@stellar/stellar-sdk";

function getLedgerKeyWasmId(
contractData: xdr.ContractDataEntry,
): xdr.LedgerKey {
const wasmHash = contractData.val().instance().executable().wasmHash();

return xdr.LedgerKey.contractCode(
new xdr.LedgerKeyContractCode({
hash: wasmHash,
}),
);
}

Now, finally we have a LedgerKey that correspond to the Wasm byte-code that has been deployed under the contractId we started out with so very long ago. This LedgerKey can be used in a final request to getLedgerEntries. In that response we will get a LedgerEntryData corresponding to a ContractCodeEntry which will contain the actual, deployed, real-life contract byte-code:

const theHashData: xdr.ContractDataEntry = await getLedgerEntries(
getLedgerKeyContractCode("C..."),
).entries[0].contractData();

const theCode: Buffer = await getLedgerEntries(getLedgerKeyWasmId(theHashData))
.entries[0].contractCode()
.code();

Fetching the ledger entry data

Once we've learned to build and parse these (which we've done above at length), the process for actually fetching them is always identical. If you know the type of key you fetched, you apply the accessor method accordingly once you've received them from the getLedgerEntries method:

const s = new Server("https://soroban-testnet.stellar.org");

// assume key1 is an account, key2 is a trustline, and key3 is contract data
const response = await s.getLedgerEntries(key1, key2, key3);

const account = response.entries[0].account();
const trustline = response.entries[1].trustline();
const contractData = response.entries[2].contractData();

Now, finally we have a LedgerKey that correspond to the Wasm byte-code that has been deployed under the ContractId we started out with so very long ago. This LedgerKey can be used in a final request to the Stellar-RPC endpoint.

{
"jsonrpc": "2.0",
"id": 12345,
"method": "getLedgerEntries",
"params": {
"keys": [
"AAAAB+QzbW3JDhlUbDVW/C+1/5SIQDstqORuhpCyl73O1vH6",
"AAAABgAAAAGfjJVEBc55drW3U87N1Py0Rw0/nlqUA6tQ6r28khEl4gAAABQAAAAB"
"AAAABgAAAAAAAAABn4yVRAXOeXa1t1POzdT8tEcNP55alAOrUOq9vJIRJeIAAAAUAAAAAQAAABMAAAAA5DNtbckOGVRsNVb8L7X/lIhAOy2o5G6GkLKXvc7W8foAAAAA"
]
}
}

Then you can inspect them accordingly. Each of the above entries follows the XDR for that LedgerEntryData structure precisely. For example, the AccountEntry is in Stellar-ledger-entries.x#L190 and you can use .seqNum() to access its current sequence number, as we've shown. In JavaScript, you can see the appropriate methods in the type definition.

Viewing and understanding XDR

If you don't want to parse the XDR out programmatically, you can also leverage both the Stellar CLI and the Stellar Lab to get a human-readable view of ledger keys and entries. For example,

echo 'AAAAAAAAAAAL76GC5jcgEGfLG9+nptaB9m+R44oweeN3EcqhstdzhQ==' | stellar xdr decode --type LedgerKey --output json-formatted
{
"account": {
"account_id": "GAF67IMC4Y3SAEDHZMN57J5G22A7M34R4OFDA6PDO4I4VINS25ZYLBZZ"
}
}

Using the Lab

The getLedgerEntries method allows you to read live ledger data directly from the network. This includes entries such as accounts, trustlines, offers, data, claimable balances, liquidity pools, and more.

It's especially useful for inspecting a contract's current state, deployed code, or any other ledger entry tied to your application. This method is often the primary way to retrieve contract-related data that may not surface through events or simulateTransaction.

To retrieve a contract’s WASM byte-code, use the ContractCode ledger entry key.

👉 View getLedgerEntries on the Lab

Using the Stellar XDR to JSON library, the getLedgerEntries method can dynamically generate input fields based on XDR-encoded data. For example, consider the following XDR string:

AAAABgAAAAHMA/50/Q+w3Ni8UXWm/trxFBfAfl6De5kFttaMT0/ACwAAABAAAAABAAAAAgAAAA8AAAAHQ291bnRlcgAAAAASAAAAAAAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAAE=

Simulate it in the Lab.

Lab Get Ledger Entries RPC

Let's submit getLedgerEntries for the following XDR string:

AAAABgAAAAGUvl2TPOjIsxuZgSyt3Lf0d6R2iNYu4rKDuULTaMKUSgAAABAAAAABAAAAAgAAAA8AAAAHQmFsYW5jZQAAAAASAAAAAAAAAABdOuyYDwLteYrby3aOykd5c12LYrui/nhbXOgtejCSYAAAAAE=

Simulate it in the Lab.

Lab Get Ledger Entries RPC