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...
Runtime version management system for dynamic implementation switching without data migration.
Version Manager implements a Strategy Pattern-based system that enables hot-swapping between different versioned implementations of the same domain (e.g., v1, v2, v3) while maintaining a unified storage layer. This approach allows seamless upgrades without downtime or migration overhead.
Pattern: Strategy + Plugin Architecture
1// protocol_fee/types.gno
2package protocol_fee
3
4type ProtocolFee interface {
5 SetFeeRatio(ratio uint64) error
6 GetFeeRatio() uint64
7}
1// protocol_fee/protocol_fee.gno
2package protocol_fee
3
4import "gno.land/p/gnoswap/version_manager"
5import "gno.land/p/gnoswap/store"
6
7var manager version_manager.VersionManager
8
9func init(cur realm) {
10 kvStore := store.NewKVStore(cur.Address())
11
12 manager = version_manager.NewVersionManager(
13 cur.PkgPath(),
14 kvStore,
15 // initializeDomainStoreFn carries the v2 interrealm marker (`_ int, rlm realm`):
16 // the leading 0 surfaces realm-threading at the call site.
17 func(_ int, rlm realm, kv store.KVStore) any {
18 return NewProtocolFeeStore(kv)
19 },
20 )
21}
22
23func GetManager() version_manager.VersionManager {
24 return manager
25}
26
27// RegisterInitializer is the crossing entry point each version package calls.
28// `cur` is the live crossing-frame realm token; it is threaded straight into the
29// version manager (the leading 0 is the v2 sentinel) so the manager can reject
30// spoofed/stale tokens via rlm.IsCurrent() and identify the caller via rlm.Previous().
31func RegisterInitializer(cur realm, initializer func(_ int, rlm realm, store any) any) {
32 if err := manager.RegisterInitializer(0, cur, initializer); err != nil {
33 panic(err)
34 }
35}
36
37// UpgradeImpl switches the active version. Authorization (admin / governance) is
38// enforced here in the /r/ realm; version_manager only rejects spoofed tokens.
39func UpgradeImpl(cur realm, packagePath string) {
40 if err := manager.ChangeImplementation(0, cur, packagePath); err != nil {
41 panic(err)
42 }
43}
1// protocol_fee/v1/v1.gno
2package v1
3
4import "gno.land/r/gnoswap/protocol_fee"
5
6type protocolFeeV1 struct {
7 store any
8}
9
10func init(cur realm) {
11 // Register this version during package initialization.
12 // `cross(cur)` invokes the domain's crossing entry point, which threads the
13 // live realm token into the version manager.
14 protocol_fee.RegisterInitializer(cross(cur), func(_ int, rlm realm, store any) any {
15 return &protocolFeeV1{store: store}
16 })
17}
18
19func (pf *protocolFeeV1) SetFeeRatio(ratio uint64) error {
20 // v1 implementation
21}
22
23func (pf *protocolFeeV1) GetFeeRatio() uint64 {
24 // v1 implementation
25}
1// protocol_fee/v2/v2.gno
2package v2
3
4type protocolFeeV2 struct {
5 store any
6}
7
8func init(cur realm) {
9 // Register v2 — inactive until explicitly activated.
10 protocol_fee.RegisterInitializer(cross(cur), func(_ int, rlm realm, store any) any {
11 return &protocolFeeV2{store: store}
12 })
13}
14
15func (pf *protocolFeeV2) SetFeeRatio(ratio uint64) error {
16 // v2 improved implementation
17}
18
19func (pf *protocolFeeV2) GetFeeRatio() uint64 {
20 // v2 improved implementation
21}
1// client code
2import "gno.land/r/gnoswap/protocol_fee"
3
4func UseFee() {
5 manager := protocol_fee.GetManager()
6 impl := manager.GetCurrentImplementation().(protocol_fee.ProtocolFee)
7
8 ratio := impl.GetFeeRatio()
9 // Use the active version's implementation
10}
1// governance or admin entry point
2func UpgradeToV2(cur realm) {
3 // Hot-swap to v2 — zero downtime. `cross(cur)` enters UpgradeImpl's crossing
4 // frame; UpgradeImpl threads the realm token into the version manager.
5 protocol_fee.UpgradeImpl(cross(cur), "gno.land/r/gnoswap/protocol_fee/v2")
6}
1. Domain package initializes version manager with KVStore
↓
2. v1 package calls RegisterInitializer (via the domain's crossing wrapper) during `init(cur realm)`
→ Manager validates the realm token (rlm.IsCurrent()) and caller domain path
→ Becomes active implementation
↓
3. v2 package calls RegisterInitializer during `init(cur realm)`
→ Registered for later activation
↓
4. v3 package calls RegisterInitializer during `init(cur realm)`
→ Registered
1. Admin/governance calls ChangeImplementation (via the domain's UpgradeImpl wrapper)
→ Authorization is enforced in the /r/ wrapper
↓
2. Version Manager validates the realm token (rlm.IsCurrent()), rejecting spoofed/stale tokens
↓
3. Version Manager retrieves v2's initializer
↓
4. Executes v2 initializer with shared KVStore
↓
5. Updates currentImplementation pointer to v2
↓
6. v2 is now the active implementation
rlm) into the manager instead of relying on runtime.CurrentRealm(). The manager validates it with rlm.IsCurrent() (rejecting spoofed/stale tokens) and identifies the registering version package via rlm.Previous()init(cur realm)The package returns errors for:
rlm.IsCurrent() == false → ErrSpoofedRealm)Upgrade DeFi protocol logic without disrupting active users:
1// Upgrade fee calculation algorithm
2protocol_fee.UpgradeImpl(cross(cur), "gno.land/r/gnoswap/protocol_fee/v2")
Test new implementations before full rollout:
1// Switch to experimental version
2protocol_fee.UpgradeImpl(cross(cur), "gno.land/r/gnoswap/protocol_fee/experimental")
3
4// Rollback if issues detected
5protocol_fee.UpgradeImpl(cross(cur), "gno.land/r/gnoswap/protocol_fee/v1")
Quickly switch to a patched version during security incidents:
1// Deploy fixed version and immediately activate
2protocol_fee.UpgradeImpl(cross(cur), "gno.land/r/gnoswap/protocol_fee/v1_hotfix")
_ int, rlm realm marker) and validated via rlm.IsCurrent()gno.land/p/gnoswap/store: KVStore with permission-based access controlPackage 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).