Allow contracts to read external contract storage keys without invoking Wasm #1958
Replies: 3 comments 2 replies
-
|
That would be helpful to have! Explicitly declaring keys as readable, I am not sure. Since the data on-chain is public, one reason to do that would be to not want certain keys to be accessed programatically from a contract. Is there a usage for such things? I suppose there is some precence in other blockchain and some rationals there to analyse. What about TTL. Is there also an auto-restore mechanism to consider if an entry is archived? |
Beta Was this translation helpful? Give feedback.
-
|
Interesting proposal. I do not need it for my work personally, but a friend tagged me to comment and I did invent CosmWasm, which also had large discussions about these raw queries. I want to share my experience there, which may be relevant input. Not pushing for any of the details of this proposal, just sharing prior art. First, we did implement them. Starting many years ago, we went with JSON objects passed between host and guest and not so many custom functions (which were tricky), so the DevX is a bit more cumbersome than Stellar (I'm quite enjoying writing Soroban once I figured it out). We allow querying other contracts with the following pub enum WasmQuery {
Smart {
contract_addr: String,
msg: Binary, // Vec<u8> with custom encoding/decoding
},
Raw {
contract_addr: String,
key: Binary, // Vec<u8> with custom encoding/decoding
},
ContractInfo {
contract_addr: String,
},
// ...
}
We have run this in production since 2020 and Pros for Raw:
Cons for Raw:
In general, this works well when one engineering team is deploying a set of contracts to separate concerns, but they want to optimize the interactions between those contracts. Since they control both the "querier" and the "queried", they can also create helpers for these calls, add tests to ensure no drift of storage layouts between versions, and coordinate simultaneous upgrades if they do want to update storage layout for the "queried" contract. I would recommend against it when you are calling into contracts you do not control. You are relying on internal details of another team's work and they might change during an upgrade, while they maintain a compatible API and don't expect to have broken any integrations. Using it when both contracts involved are not in the same mono-repo is much more dangerous. That said, the CosmWasm community did define some "interfaces" like cw2 to be standarized storage layouts, which is not present in all contracts, but if a contract implements this "interface" it is also committing to a specific storage layout and thus can be read by raw queries. The info we stored in cw2 is similar to information added in meta tags in the wasm itself in Soroban (which I actually prefer). This concept of "interfaces" that define a storage layout can be used for example defining one storage location for any rebasable token to share the ratio - and thus the contract implementing that interface guaranteed a stable storage layout for the life of the contact and this could allow safe cross-contract raw calls for contracts between multiple teams - when it is really just reading one standardized data structure. I would recommend against this as a general structure as wasm apis (method, params) style give a much larger space for innovation, but for very stable, well defined concepts, this could be a useful optimization. In summary: From my experience in CosmWasm, adding raw cross-contract storage queries will not break anything and can be safely implemented at a much lower gas cost. However, there are many caveats to their use and require a much tighter coupling between the two contracts involved. Recommendation to add significant documentation on "when to use" for this call if implemented. |
Beta Was this translation helpful? Give feedback.
-
|
I am thinking that this links to the discussion we had around what to export or not on the bindings. That is also an argument to have a specific marker as to declare a public API vs a private one in the sense of guaranteeing a certain compatibility for dependent. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Read-only external storage reads for Soroban contracts
I opened prototype PRs for read-only external contract storage access:
I am opening this discussion because this changes what contracts can read during execution and likely needs a CAP. The PRs above contain the prototype implementation and review comments that raised the protocol and security questions.
Problem
A lending protocol on Soroban usually splits execution, market state, oracle configuration, and governance-controlled risk settings across separate contracts.
The execution contract handles supply, withdraw, borrow, repay, liquidation, and reward flows. Other contracts hold market parameters, reserve state, debt indexes, collateral factors, caps, pause flags, and oracle references. This split keeps contracts smaller and makes audits easier because reviewers can separate execution logic from configuration and governance state.
Today the execution contract must invoke another contract to read one stored value from that module. A borrow or liquidation flow can need several such reads before it can price collateral, update indexes, check caps, and validate risk flags. Many of those reads do not need target contract code. The caller needs a value under a known key.
Getter functions solve this, but they add Wasm interface surface and boilerplate to contracts that only need to expose stored values.
Proposal
Add read-only host functions that let a contract read another contract's storage by target contract address, key, and storage type:
has_external_contract_data(contract, key, storage_type) -> Boolget_external_contract_data(contract, key, storage_type) -> ValThe host would enforce the transaction footprint. Persistent and temporary reads would record the target contract-data ledger key as read-only. Instance reads, if included, would record the target contract instance ledger key as read-only.
The SDK could expose this as:
env.storage().persistent().has_external(&contract, &key)env.storage().persistent().get_external::<_, V>(&contract, &key)and the same shape for temporary and instance storage.
Questions
Beta Was this translation helpful? Give feedback.
All reactions