- a key (the identifier for the data),
- a value (the data itself), and
- a Time To Live (TTL), which is how many ledgers remain until the entry is no longer live. (The absolute cutoff is stored on the entry itself as its
liveUntilLedger.)
The extension process
- Build the ledger key for the entry you want to extend.
- Build a transaction containing an
extendFootprintTtloperation with your target TTL, and attach the Soroban transaction data. - Prepare the transaction (this runs simulation to fill in the resources and fee for you), sign it with your secret key, and submit it to the network.
extendFootprintTtl sets a floor, not an exact value. Per the ExtendFootprintTTLOp semantics, the operation guarantees each entry in the read-only footprint lives for at least extendTo ledgers from now. Entries that already live longer than your target are left alone, so a repeated extension is harmless.There’s also a ceiling: an entry can only be extended up to the network’s maximum TTL, which is a network parameter (check the network limits in the Stellar Lab for the current value).Extending a persistent entry
The example below assumes your contract keys its data with a symbol, which is the common case. If your contract uses a vec, a map, or a struct as its storage key, build theScVal that matches instead of reaching for scvSymbol.
Extending the contract instance
Contract.getFootprint() hands you the ledger key for the deployed contract instance, so you don’t have to assemble that one by hand.
This extends the contract instance only. The Wasm code the instance points to is a separate ledger entry with its own TTL, and an
extendFootprintTtl operation only touches the keys you actually put in the read-only footprint. A live instance backed by archived code still leaves your contract unusable, so the next section covers the code entry too.If you’re coming from the Rust side, this is a real difference worth internalizing: inside a contract, env.storage().instance().extend_ttl() extends the instance and the code together. The operation used here does not.Extending the contract code
The code entry is keyed by the Wasm hash rather than by the contract address, and there’s no local way to derive one from the other. That means one extra round trip: read the contract’s instance entry from the network, pull the Wasm hash out of its executable, and build acontractCode ledger key from it.
It’s worth knowing every contract deployed from the same Wasm shares that single code entry, so extending it benefits all of them at once.
Extending all three together
A Soroban operation has to be the only operation in its transaction, so you might expect to need one transaction per entry. Happily, you don’t. A singleExtendFootprintTTLOp extends every entry listed in the transaction’s read-only footprint, applying the same extendTo target to all of them. When a batch of entries should live until the same ledger, list them all in one footprint and extend them together. Here’s all three at once, reusing the getWasmHash helper from the previous section:
extendTo is the one real constraint. Entries that need different target TTLs can’t share an operation, so extend each one in its own transaction. That’s exactly what the extendPersistentEntryTTL, extendContractInstanceTTL, and extendContractCodeTTL helpers above do: each builds its own operation, so each can carry its own extendTo target.
Batching keys into one footprint grows the transaction’s resource usage, since
diskReadBytes has to cover the serialized size of every entry in the read-only set. server.prepareTransaction works that out for you, but a large enough batch can bump into the network’s transaction limits. If that happens, split the batch across a few transactions.