getLedgerEntries
For reading the current value of ledger entries directly.
This method enables querying live ledger state: accounts, trustlines, offers, data, claimable balances, and liquidity pools. It also provides direct access to inspect a contract's current state, its code, or any other ledger entry. This serves as a primary method to access your contract data which may not be available via events or simulateTransaction
.
To fetch contract wasm byte-code, use the ContractCode ledger entry key.
Params
(2)Please note that parameter structure within the request must contain named parameters as a by-name object, and not as positional arguments in a by-position array
1. keys (required)
Array containing the keys of the ledger entries you wish to retrieve. (an array of serialized base64 strings)
An array of LedgerKeys. The maximum number of ledger keys accepted is 200.
2. xdrFormat
Lets the user choose the format in which the response should be returned - either as unpacked JSON or as base64-encoded XDR strings. Note that you should not rely on any schema for the JSON, as it will change when the underlying XDR changes.
Specifies whether XDR should be encoded as Base64 (default or 'base64') or JSON ('json').
Result
(getLedgerEntriesResult)The sequence number of the latest ledger known to Stellar RPC at the time it handled the request.
Array of objects containing all found ledger entries
The LedgerKey corresponding to the ledger entry (base64 string).
The key's current LedgerEntryData value (base64 string).
The ledger sequence number of the last time this entry was updated.
Sequence number of the ledger.
Examples
Example request to the getNetwork
method for a Counter(Address)
ledger entry.
Request
- cURL
- JavaScript
- Python
- JSON
curl -X POST \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 8675309,
"method": "getLedgerEntries",
"params": {
"keys": [
"AAAABgAAAAHMA/50/Q+w3Ni8UXWm/trxFBfAfl6De5kFttaMT0/ACwAAABAAAAABAAAAAgAAAA8AAAAHQ291bnRlcgAAAAASAAAAAAAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAAE="
]
}
}' \
https://soroban-testnet.stellar.org | jq
let requestBody = {
"jsonrpc": "2.0",
"id": 8675309,
"method": "getLedgerEntries",
"params": {
"keys": [
"AAAABgAAAAHMA/50/Q+w3Ni8UXWm/trxFBfAfl6De5kFttaMT0/ACwAAABAAAAABAAAAAgAAAA8AAAAHQ291bnRlcgAAAAASAAAAAAAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAAE="
]
}
}
let res = await fetch('https://soroban-testnet.stellar.org', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
})
let json = await res.json()
console.log(json)
import json, requests
res = requests.post(https://soroban-testnet.stellar.org, json={
"jsonrpc": "2.0",
"id": 8675309,
"method": "getLedgerEntries",
"params": {
"keys": [
"AAAABgAAAAHMA/50/Q+w3Ni8UXWm/trxFBfAfl6De5kFttaMT0/ACwAAABAAAAABAAAAAgAAAA8AAAAHQ291bnRlcgAAAAASAAAAAAAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAAE="
]
}
})
print(json.dumps(res.json(), indent=4))
{
"jsonrpc": "2.0",
"id": 8675309,
"method": "getLedgerEntries",
"params": {
"keys": [
"AAAABgAAAAHMA/50/Q+w3Ni8UXWm/trxFBfAfl6De5kFttaMT0/ACwAAABAAAAABAAAAAgAAAA8AAAAHQ291bnRlcgAAAAASAAAAAAAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAAE="
]
}
}
Result
{
"jsonrpc": "2.0",
"id": 8675309,
"result": {
"entries": [
{
"key": "AAAAB+qfy4GuVKKfazvyk4R9P9fpo2n9HICsr+xqvVcTF+DC",
"xdr": "AAAABgAAAAAAAAABzAP+dP0PsNzYvFF1pv7a8RQXwH5eg3uZBbbWjE9PwAsAAAAQAAAAAQAAAAIAAAAPAAAAB0NvdW50ZXIAAAAAEgAAAAAAAAAAIOHWwMbBgBiAnwRt4k9nChmEOoSuLCVs2eqK9Qub+hgAAAABAAAAAwAAAAw=",
"lastModifiedLedgerSeq": 2552504
}
],
"latestLedger": 2552990
}
}
Building ledger keys
The Stellar ledger is, on some level, essentially a key-value store. The keys are instances of LedgerKey
and the values are instances of LedgerEntry
. 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.
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.
Types of LedgerKey
s
The source of truth should always be the XDR defined in the protocol. LedgerKey
s are a union type defined in Stellar-ledger-entries.x. There are 10 different forms a ledger key can take:
- Account: holistically defines a Stellar account, including its balance, signers, etc. (see Accounts)
- Trustline: defines a balance line to a non-native asset issued on the network (see
changeTrustOp
) - Offer: defines an offer made on the Stellar DEX (see Liquidity on Stellar)
- Account Data: defines key-value data entries attached to an account (see
manageDataOp
) - Claimable Balance: defines a balance that may or may not actively be claimable (see Claimable Balances)
- Liquidity Pool: defines the configuration of a native constant liquidity pool between two assets (see Liquidity on Stellar)
- Contract Data: defines a piece of data being stored in a contract under a key
- Contract Code: defines the Wasm bytecode of a contract
- Config Setting: defines the currently active network configuration
- TTL: defines the time-to-live of an associated contract data or code entry
We're going to focus on a subset of these for maximum value, but once you understand how to build and parse some keys and entries, you can extrapolate to all of them.
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 (such as Circle's USDC). 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(
"USDC",
"GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
).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
.balance()
.toString()}`,
);
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:
- Python
- TypeScript
from stellar_sdk import xdr, scval, Address
def get_ledger_key_symbol(contract_id: str, symbol_text: str) -> str:
ledger_key = xdr.LedgerKey(
type=xdr.LedgerEntryType.CONTRACT_DATA,
contract_data=xdr.LedgerKeyContractData(
contract=Address(contract_id).to_xdr_sc_address(),
key=scval.to_symbol(symbol_text),
durability=xdr.ContractDataDurability.PERSISTENT
),
)
return ledger_key.to_xdr()
print(
get_ledger_key_symbol(
"CCPYZFKEAXHHS5VVW5J45TOU7S2EODJ7TZNJIA5LKDVL3PESCES6FNCI",
"COUNTER"
)
)
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:
- First, we look up the contract itself, to see which code hash it is referencing.
- Then, we can look up the raw Wasm byte-code using that hash.
1. Find the ledger key for the contract instance
- Python
- TypeScript
from stellar_sdk import xdr, Address
def get_ledger_key_contract_code(contract_id: str) -> xdr.LedgerKey:
return xdr.LedgerKey(
type=xdr.LedgerEntryType.CONTRACT_DATA,
contract_data=xdr.LedgerKeyContractData(
contract=Address(contract_id).to_xdr_sc_address(),
key=xdr.SCVal(xdr.SCValType.SCV_LEDGER_KEY_CONTRACT_INSTANCE),
durability=xdr.ContractDataDurability.PERSISTENT
)
)
print(get_ledger_key_contract_code(
"CCPYZFKEAXHHS5VVW5J45TOU7S2EODJ7TZNJIA5LKDVL3PESCES6FNCI"
))
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.
- Python
- TypeScript
from stellar_sdk import xdr
def get_ledger_key_wasm_id(
# received from getLedgerEntries and decoded
contract_data: xdr.ContractDataEntry
) -> xdr.LedgerKey:
# First, we dig the wasm_id hash out of the xdr we received from RPC
wasm_hash = contract_data.val.instance.executable.wasm_hash
# Now, we can create the `LedgerKey` as we've done in previous examples
ledger_key = xdr.LedgerKey(
type=xdr.LedgerEntryType.CONTRACT_CODE,
contract_code=xdr.LedgerKeyContractCode(
hash=wasm_hash
),
)
return ledger_key
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();
Actually 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 contractData = response.entries[2].contractData();
const contractCode = response.entries[1].contractCode();
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#L191
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"
}
}