[SEP-49] Upgradeable Contracts #1670
Replies: 5 comments 10 replies
-
I'm not very sure of the inner-workings of the SDK, but is it possible to only auto-embed this metadata if there is a defined
It occurs to me that similar to how sometimes deployer contracts were used prior to protocol 22 to "simulate" constructor-like behavior, it might be possible to utilize an "upgrader" contract to both perform the If it is possible, would that even be a desired practice? Would that be a "dangerous" practice? Particularly if it's an anti-pattern, should this SEP steer developers away from that? Or, is that an entirely out-of-scope train of thought?
I wonder if there's any value in adding more detailed recommendations or best practices for handling rollbacks. I assume we wouldn't want or need to emit a specific event in this instance, right? Anyone listening for the upgrade system events could trace that one update happened, followed by another and we end up at the original Wasm hash. Essentially, it can be considered and treated as "just another upgrade," except that the version number would decrease. |
Beta Was this translation helpful? Give feedback.
-
Why is this an issue? For example, if I have a v0.0.1, need to patch it with v0.0.2 I would need to strip the constructor from the new version? |
Beta Was this translation helpful? Give feedback.
-
I'd like to see if we can embed this automatically using the SDK. The SDK already embeds two meta fields by default into every build:
It feels natural to me that the version of the crate would end up alongside these. The version has utility outside of upgradeability, because even a non-upgradeable contract may have multiple versions if it is instead deployed using a proxy pattern, or even if no form of upgradeability is supported but multiple versions of the contract can exist on chain. The version serves as a good companion meta field to sit alongside other fields like |
Beta Was this translation helpful? Give feedback.
-
I see the allure of creating a standard around a function to call to do the upgrade. It would allow tooling like the Lab or stellar-cli to add a feature to support upgrading specifically, for contracts implementing the SEP. In the CLI that would in use look something like:
However, it's also worth noting that anyone implementing the function above, or any function that accepts the new wasm hash, could simply call it with the existing invoke function meaning tooling doesn't really need dedicated upgrade functionality. E.g.:
It's not obvious to me yet where the win is in standardising on a specific function for upgrades, since the entity upgrading is often the contract operator, and they could call any function they need to, with whatever parameters were needed at the time. For contracts that are not managed by a single operator, upgrading may require a more complex interface, such as first registering the intent to upgrade, and then later executing it after some time lock delay, and/or a certain number of votes. How do we see this standard being used in those situations? |
Beta Was this translation helpful? Give feedback.
-
Based on the discussion above, this SEP has evolved from proposing a standard upgrade interface and versioning format into a broader set of community guidelines and best practices for safe and transparent contract upgrades. Here is the new version: Upgradeable Contracts SEPPreamble
SummaryCommunity guidelines and recommendations for safely upgrading the WASM bytecode of Soroban smart contracts. MotivationSoroban contracts are mutable by default unlike many other smart contract platforms such as Ethereum. Mutability in the context of Soroban refers to the ability of a smart contract to modify its WASM bytecode, thereby altering its function interface, execution logic, or metadata. However, there are cases where immutable contracts are preferable, such as when immutability guarantees are essential for trustless applications or when a contract’s logic must remain fixed to comply with regulatory requirements. In such scenarios, non-upgradability ensures that the contract's behavior remains predictable and secure. Soroban provides a built-in, protocol-level defined mechanism for contract upgrades, allowing contracts to upgrade themselves if they are explicitly designed to do so, thus delegating the decision for it to the application layer. This native upgrade mechanism offers significant advantages. One of them is the flexibility it offers to contract developers who can choose to make the contract immutable by simply not provisioning upgradability mechanics. On the other hand, providing upgradability on a protocol level significantly reduces the risk surface. This becomes particularly evident when compared to Ethereum, which lacks native support for upgradability, necessitating the development of various upgradability patterns, each with its own trade-offs. Despite the availability of tooling, handling upgrades in Ethereum remains a complex and error-prone process. While Soroban’s built-in upgradability eliminates many of these challenges, certain caveats must still be considered. AbstractThis SEP defines a set of recommendations for Soroban smart contracts that includes:
RecommendationsVersioningEach contract can include a version identifier in its metadata to facilitate tracking and validation. The version of the WASM bytecode can be stored using
The key Versioning in metadata is preferred over defining a dedicated
UpgradingThis SEP does not propose a standard
Example (pseudo-code): #[contractimpl]
impl MyContract {
pub fn upgrade(e: &Env, new_wasm_hash: BytesN<32>, operator: Address) {
operator.require_auth();
let owner: Address = e.storage().instance().get(&OWNER).unwrap();
if (owner != operator) {
panic_with_error!(e, ContractError::Unauthorized)
}
e.deployer().update_current_contract_wasm(new_wasm_hash);
}
} MigrationsWhat is a migration?A migration refers to any change made to a contract’s storage layout. This is typically required after a contract upgrade that modifies how data is structured or interpreted. Handling migrationsSoroban’s architecture prevents calling functions in the newly deployed WASM within the same invocation. Therefore, pre- or post-upgrade housekeeping (such as data migrations) must be handled in a separate step or context. Recommended best practices include:
Example (pseudo-code): pub const MIGRATE: Symbol = symbol_short!("migrate");
#[contractimpl]
impl Upgrader {
pub fn upgrade_and_migrate(
e: Env,
contract_address: Address,
operator: Address,
wasm_hash: BytesN<32>,
migration_data: soroban_sdk::Vec<Val>,
) {
let contract_client = UpgradeableClient::new(&e, &contract_address);
// the parameters of `upgrade()` can vary;
// it is recommmended to explicitly name it `upgrade()`
contract_client.upgrade(&wasm_hash, &operator);
// If this contract is meant to be used for upgrading contracts with a defined `migrate()` funciton,
// it can be invoked with a regular call:
// `contract_client.migrate(param1, param2, param3)`
e.invoke_contract::<()>(&contract_address, &MIGRATE, migration_data);
}
}
RollbacksOnce a smart contract is deployed, it cannot be rolled back like in traditional systems. Any incorrect logic must be corrected with a new contract version.
Transparency and TrustworthinessTo enhance safety and predictability when upgrading contracts, the following practices are recommended:
Further ConsiderationsTo mitigate risks associated with contract upgrades, the following points shoud be also taken into account:
These checks can be implemented in upgrade tooling and libraries, providing warnings or requiring explicit confirmations for potential breaking changes. Changelog
|
Beta Was this translation helpful? Give feedback.
-
Upgradeable Contracts SEP
Preamble
Summary
A standard for smart contracts defining the specifications on how to upgrade their WASM bytecode.
Motivation
Soroban contracts are mutable by default unlike many other smart contract platforms such as Ethereum. Mutability in the context of Soroban refers to the ability of a smart contract to modify its WASM bytecode, thereby altering its function interface, execution logic, or metadata.
However, there are cases where immutable contracts are preferable, such as when immutability guarantees are essential for trustless applications or when a contract’s logic must remain fixed to comply with regulatory requirements. In such scenarios, non-upgradeability ensures that the contract's behavior remains predictable and secure.
Soroban provides a built-in, protocol-level defined mechanism for contract upgrades, allowing contracts to upgrade themselves if they are explicitly designed to do so, thus delegating the decision for it to the application layer.
This native upgrade mechanism offers significant advantages. One of them is the flexibility it offers to contract developers who can choose to make the contract immutable by simply not provisioning upgradability mechanics. On the other hand, providing upgradability on a protocol level significantly reduces the risk surface. This becomes particularly evident when compared to Ethereum, which lacks native support for upgradability, necessitating the development of various upgradability patterns, each with its own trade-offs. Despite the availability of tooling, handling upgrades in Ethereum remains a complex and error-prone process.
While Soroban’s built-in upgradeability eliminates many of these challenges, certain caveats must still be considered. Contract upgrades require additional validation and verification to ensure security and compatibility.
Considerations for Upgradability
To mitigate risks associated with contract upgrades, we should establish guidelines for additional validation checks. Some potential checks include:
These checks can be implemented in upgrade tooling, providing warnings or requiring explicit confirmations for potential breaking changes.
This SEP proposes a minimal standardization of the upgrade interface to facilitate these efforts, ensuring a secure and well-defined approach to contracts upgradability.
Abstract
This SEP defines a standardized upgradability mechanism for Soroban contracts that includes:
upgrade()
function.Specification
Versioning
Each contract should include a version identifier in its metadata to facilitate tracking and validation. The version of the WASM bytecode is stored using
soroban_sdk::contractmeta!
where:binver
,The Soroban SDK may automatically embed this metadata by reading from the crate version to streamline adoption.
Interface
Example Usage
Pre- and Post-Upgrade Handling
Soroban’s architecture prevents calling functions in the newly deployed WASM within the same invocation. Therefore, pre- or post-upgrade housekeeping (such as data migrations) must be handled separately.
Recommended best practices:
migrate()
function for pre- or post-upgrade actions.Design Rationale
version()
function for the following reasons:contractmetav0
that’s already well leveraged by the Soroban SDK and the executable version seems a logical candidate to get stored therebinver
is chosen for consistency with the already existingrsver
andrssdkver
in the metadata.upgrade()
function as this is specific for every implementation.upgrade()
functions also facilitates rollbacks if needed.Beta Was this translation helpful? Give feedback.
All reactions