const ErrSpoofedRealm
ErrSpoofedRealm is returned when the supplied realm token does not match the live crossing frame (rlm.IsCurrent() == false). It signals a stale or spoofed token captured in an earlier frame.
Package version\_manager provides a runtime version management system for dynamic implementation switching without da...
Package version_manager provides a runtime version management system for dynamic implementation switching without data migration. It implements the Strategy Pattern combined with Plugin Architecture to enable hot-swapping between different versioned implementations of the same domain.
## Overview
Version Manager enables seamless upgrades by allowing multiple versioned implementations (v1, v2, v3) to coexist and share a unified storage layer. The domain (proxy) realm owns that storage; the manager records version initializers and swaps the active implementation reference without granting implementation realms direct write permission.
Key components of this package include:
## Key Features
## Architecture Pattern
The package implements two complementary design patterns:
## Workflow
Typical usage of the version_manager package includes the following steps:
## Example Usage
### Step 1: Define Domain Interface
```gno // protocol_fee/types.gno package protocol_fee
```
### Step 2: Create Version Manager
```gno // protocol_fee/protocol_fee.gno package protocol_fee
import (
)
1var manager version_manager.VersionManager
2
3func init(cur realm) {
4 kvStore := store.NewKVStore(cur.Address())
5
6 manager = version_manager.NewVersionManager(
7 cur.PkgPath(),
8 kvStore,
9 func(_ int, rlm realm, kv store.KVStore) any {
10 return NewProtocolFeeStore(kv)
11 },
12 )
13}
14
15func GetManager() version_manager.VersionManager {
16 return manager
17}
```
### Step 3: Implement Version 1
```gno // protocol_fee/v1/v1.gno package v1
import "gno.land/r/gnoswap/protocol_fee"
type protocolFeeV1 struct {
1store any
}
1func init(cur realm) {
2 // Register this version during package initialization.
3 protocol_fee.RegisterInitializer(cross(cur), func(_ int, rlm realm, store any) any {
4 return &protocolFeeV1{store: store}
5 })
6}
7
8func (pf *protocolFeeV1) SetFeeRatio(ratio uint64) error {
9 // v1 implementation
10 return nil
11}
12
13func (pf *protocolFeeV1) GetFeeRatio() uint64 {
14 // v1 implementation
15 return 0
16}
```
### Step 4: Implement Version 2
```gno // protocol_fee/v2/v2.gno package v2
import "gno.land/r/gnoswap/protocol_fee"
type protocolFeeV2 struct {
1store any
}
1func init(cur realm) {
2 // Register v2 - inactive until explicitly activated.
3 protocol_fee.RegisterInitializer(cross(cur), func(_ int, rlm realm, store any) any {
4 return &protocolFeeV2{store: store}
5 })
6}
7
8func (pf *protocolFeeV2) SetFeeRatio(ratio uint64) error {
9 // v2 improved implementation
10 return nil
11}
12
13func (pf *protocolFeeV2) GetFeeRatio() uint64 {
14 // v2 improved implementation
15 return 0
16}
```
### Step 5: Use Active Implementation
```gno // client code import "gno.land/r/gnoswap/protocol_fee"
1func UseFee() {
2 manager := protocol_fee.GetManager()
3 impl := manager.GetCurrentImplementation().(protocol_fee.ProtocolFee)
4
5 ratio := impl.GetFeeRatio()
6 // Use the active version's implementation
7}
```
### Step 6: Switch Versions at Runtime
```gno // governance or admin function
1func UpgradeToV2(cur realm) {
2 // Hot-swap to v2 - zero downtime.
3 protocol_fee.UpgradeImpl(cross(cur), "gno.land/r/gnoswap/protocol_fee/v2")
4}
```
## Registration Flow
The version registration process follows this sequence:
## Version Switching Flow
When switching versions, the following steps occur:
## Storage Access Model
The version manager keeps storage ownership with the domain (proxy) realm:
## Security
Domain-scoped security ensures that only authorized packages can register:
## Error Handling
The package returns errors for:
## Best Practices
## Use Cases
### Protocol Upgrades
Upgrade DeFi protocol logic without disrupting active users:
1manager.ChangeImplementation(0, cur, "gno.land/r/gnoswap/protocol_fee/v2")
### A/B Testing
Test new implementations before full rollout:
1// Switch to experimental version
2manager.ChangeImplementation(0, cur, "gno.land/r/gnoswap/protocol_fee/experimental")
3
4// Rollback if issues detected
5manager.ChangeImplementation(0, cur, "gno.land/r/gnoswap/protocol_fee/v1")
### Emergency Response
Quickly switch to a patched version during security incidents:
1manager.ChangeImplementation(0, cur, "gno.land/r/gnoswap/protocol_fee/v1_hotfix")
## Limitations and Considerations
## Related Packages
Package version_manager is intended for use in Gno smart contracts requiring dynamic, upgradeable implementations with zero-downtime version switching.
Package version_manager implements a runtime version management system using the Strategy Pattern. It enables dynamic switching between different implementation versions of the same domain (e.g., v1, v2, v3) while maintaining a unified storage layer. This approach allows for seamless upgrades without migration overhead.
Key Features:
Architecture Pattern: Strategy + Plugin Architecture
ErrSpoofedRealm is returned when the supplied realm token does not match the live crossing frame (rlm.IsCurrent() == false). It signals a stale or spoofed token captured in an earlier frame.
NewVersionManager creates a new version manager instance for a specific domain. This should be called once per domain during system initialization.
Parameters:
domainPath: The base package path for the domain (e.g., "gno.land/r/gnoswap/protocol_fee") Used for access control to ensure only authorized packages can register
kvStore: The shared key-value store that all versions will access The domain realm (proxy) is the owner and has write permission to this store
initializeDomainStoreFn: A factory function that wraps the KVStore into a domain-specific storage interface This abstraction allows each version to work with a familiar storage API Example: func(_ int, rlm realm, kvStore store.KVStore) any { return NewProtocolFeeStore(kvStore) }
Returns:
Usage Pattern:
1type VersionManager interface {
2 // RegisterInitializer registers a version's implementation.
3 // Must be called by each version package during initialization.
4 // First registration becomes the active implementation.
5 // Subsequent registrations are retained for later switching.
6 //
7 // The leading `_ int, rlm realm` is the v2 interrealm-spec marker pattern:
8 // the `0` sentinel surfaces at every call site so the realm threading is
9 // visible to the reader, and rlm is the live crossing-frame token that
10 // the implementation validates via rlm.IsCurrent() and reads via
11 // rlm.Previous() to identify the version package.
12 RegisterInitializer(_ int, rlm realm, initializer func(_ int, rlm realm, store any) any) error
13
14 // ChangeImplementation switches the active version at runtime.
15 // This enables hot-swapping without downtime or data migration while storage
16 // ownership stays with the domain KVStore.
17 //
18 // The leading `_ int, rlm realm` is the v2 interrealm-spec marker pattern;
19 // rlm is validated via rlm.IsCurrent() to reject spoofed/stale tokens.
20 // Upgrade ACLs live in the wrapping /r/ realm, not here.
21 ChangeImplementation(_ int, rlm realm, packagePath string) error
22
23 // GetDomainPath returns the base domain path (e.g., "gno.land/r/gnoswap/protocol_fee")
24 GetDomainPath() string
25
26 // GetInitializers returns all registered version initializers
27 GetInitializers() map[string]func(_ int, rlm realm, store any) any
28
29 // GetCurrentPackagePath returns the package path of the active implementation
30 GetCurrentPackagePath() string
31
32 // GetCurrentImplementation returns the active version instance
33 // The caller should type-assert this to the domain-specific interface
34 GetCurrentImplementation() any
35}VersionManager defines the interface for managing multiple versioned implementations of a domain. It enables the Strategy Pattern at the package level, allowing runtime switching between different versions (v1, v2, v3, etc.) without requiring data migration.
Design Goals:
Implementation Note: The actual implementations of each version must satisfy a common domain interface defined by the specific domain (e.g., ProtocolFee interface for protocol_fee domain).