Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

v1 source pure

Package store provides a domain-specific key-value storage system with permission-based write access control for Gno ...

Overview

Package store provides a domain-specific key-value storage system with permission-based write access control for Gno smart contracts.

## Overview

Each domain (e.g., pool, position, router, staker) creates its own KVStore instance bound to a unique domain address. That domain address is the primary authority over the store: it can write, manage the authorized-writer set, and is itself implicitly authorized as a writer.

Key components of this package:

  1. **KVStore Interface** - The contract for domain-specific storage operations.
  2. **kvStore Implementation** - Concrete implementation with permission management and data storage.
  3. **Write-only Permission System** - Reads are public within the package; only writes (Set, Delete, ACL changes) are gated.
  4. **Type-Safe Getters** - Typed getter methods with runtime type casting and validation.

## Key Features

  • **Domain Isolation**: Each domain has its own isolated storage space identified by its domain address. Keys are internally prefixed with that address so domains cannot collide.
  • **Per-Realm Write ACL**: The store maintains an explicit set of realms authorized to write. The domain owner is in the set by default.
  • **Type-Safe Operations**: Specialized getters (`GetInt64`, `GetString`, `GetAddress`, ...) with automatic casting and structured errors.
  • **Realm-Frame Auth Checks**: Every write call accepts a `realm` value representing the live crossing frame; the store verifies that frame before consulting the ACL, defending against replayed or captured `realm` values.
  • **Authorized Caller Management**: Dynamic addition, update, and removal of authorized callers with specific permissions.

## Permission Model

The package defines one assignable permission level:

  • **Write (1)**: Allowed to call `Set` and `Delete`.

The zero value of `Permission` is reserved and rejected by registration APIs. Code callers not in the authorized-caller table have no write access. Current user/EOA realms bypass the ACL for Set and Delete after the live-frame check. Read methods do not enforce caller permissions; realm-level APIs must gate sensitive reads themselves. The domain address (owner) is always treated as authorized and cannot be removed.

## Realm Threading

Every mutating method takes two leading parameters:

Example
1func (k *kvStore) Set(_ int, rlm realm, key string, value any) error

The leading `_ int` is a deliberate sentinel: at the call site it shows up as a `0`, making it visually obvious that a `realm` value is being threaded through. `rlm` is the live crossing frame, and `rlm.IsCurrent()` is checked first in every mutating method to reject stale or spoofed tokens.

The v1 -> v2 mapping for the two realm-identity expressions is straightforward:

  • `runtime.CurrentRealm().Address()` -> `rlm.Address()` (the current realm)
  • `runtime.PreviousRealm().Address()` -> `rlm.Previous().Address()` (the caller)

Which one a given method checks is a per-method domain decision, not a v2 framework rule. The store currently checks:

  • `Set` / `Delete`: `rlm.Address()` (the realm performing the write, i.e. the domain realm calling into the store) against the write ACL for code callers. Current user/EOA realms (`!rlm.IsCode()`) bypass the ACL after `rlm.IsCurrent()` validation, so they can populate or delete state.
  • `AddAuthorizedCaller`, `UpdateAuthorizedCaller`, `RemoveAuthorizedCaller`: `rlm.Address()` (the current realm) against the domain address.

## Workflow

  1. **Initialization**: Create a store with `NewKVStore(domainAddress)`.
  2. **Data Operations**: Use `Set` / `Get` (and typed getters) for I/O.
  3. **Permission Management**: From the domain realm, call `AddAuthorizedCaller` to grant write access to additional realms.
  4. **Type-Safe Retrieval**: Prefer typed getters (`GetInt64`, `GetString`, etc.) over raw `Get` whenever the stored type is known.

## Example Usage

```gno package examplerealm

import (

Example
1"gno.land/p/gnoswap/store/v1"

)

Example
 1func Configure(cur realm, routerAddr address) error {
 2    // Create a KVStore owned by the current (pool) realm.
 3    kv := store.NewKVStore(cur.Address())
 4
 5    // Persist some values. `0, cur` threads the live realm frame through.
 6    if err := kv.Set(0, cur, "totalLiquidity", uint64(1_000_000)); err != nil {
 7        return err
 8    }
 9    if err := kv.Set(0, cur, "poolName", "ETH-USDC"); err != nil {
10        return err
11    }
12
13    // Grant write access to the router realm.
14    if err := kv.AddAuthorizedCaller(0, cur, routerAddr, store.Write); err != nil {
15        return err
16    }
17
18    // Reads are public -- no realm needed.
19    liquidity, err := kv.GetUint64("totalLiquidity")
20    if err != nil {
21        return err
22    }
23    _ = liquidity
24
25    return nil
26}

```

## Permission Checks at a Glance

The store automatically verifies permissions during operations:

  • **Read Operations** (`Get`, `GetInt64`, `GetString`, ...): no permission check; reads are public within the package.
  • **Write Operations** (`Set`, `Delete`): require the calling realm -- `rlm.Address()` -- to be in the write ACL when the caller is code. Current user/EOA realms (`!rlm.IsCode()`) bypass the ACL after the live frame check.
  • **Management Operations** (`AddAuthorizedCaller`, `UpdateAuthorizedCaller`, `RemoveAuthorizedCaller`): require the current realm -- `rlm.Address()` -- to equal the domain address.

## Key Prefixing

Internally, all keys are prefixed with the domain address to keep domains isolated:

Example
1Stored key: "{domainAddress}:{userKey}"

This prevents key collisions between domains that share a runtime.

## Errors

The package defines several error types:

  • `ErrWritePermissionDenied` - the calling code realm (`rlm.Address()`) is not in the write ACL. Current user/EOA realms bypass the ACL for Set and Delete after `rlm.IsCurrent()` validation.
  • `ErrUpdatePermissionDenied` - ACL change attempted from a current realm (`rlm.Address()`) other than the domain address.
  • `ErrAuthorizedCallerAlreadyRegistered` - Adding a caller that already exists.
  • `ErrAuthorizedCallerNotFound` - Updating or removing a caller that was never registered.
  • `ErrInvalidPermission` - Permission value other than `Write` was passed.
  • `ErrFailedCast` - Typed getter saw a value whose runtime type does not match.
  • `ErrSpoofedRealm` - The supplied `realm` is not the live crossing frame.

## Limitations and Considerations

  • All stored values are of type `any`, so retrieval requires either a typed getter or a cast.
  • Read methods are not permission-gated. Add realm-level checks for sensitive reads.
  • ACL management can only be performed by the domain realm itself.
  • The domain address is implicitly authorized and cannot be removed.
  • Keys are automatically prefixed; callers should not encode the domain address into their own keys.

Package store is intended for use in Gno smart contracts that need isolated, write-gated storage for distinct protocol components.

Constants 2

const ErrAuthorizedCallerAlreadyRegistered, ErrAuthorizedCallerNotFound, ErrInvalidPermission, ErrKeyNotFound, ErrWritePermissionDenied, ErrUpdatePermissionDenied, ErrFailedCast, ErrSpoofedRealm

 1const (
 2	// Authorization errors
 3	ErrAuthorizedCallerAlreadyRegistered = "authorized caller already registered"
 4	ErrAuthorizedCallerNotFound          = "authorized caller not found"
 5	ErrInvalidPermission                 = "invalid permission"
 6
 7	// Data access errors
 8	ErrKeyNotFound            = "key not found"
 9	ErrWritePermissionDenied  = "write permission denied"
10	ErrUpdatePermissionDenied = "update permission denied"
11	ErrFailedCast             = "failed to cast"
12	ErrSpoofedRealm           = "rlm does not match the current crossing frame"
13)
source

Functions 1

func NewKVStore

1func NewKVStore(domainAddress address) KVStore
source

NewKVStore creates a new kvStore instance for a specific domain. domainAddress is the address of the domain realm that owns this store.

Parameters:

  • domainAddress: Address of the owning domain realm; it is initially granted Write permission.

Returns:

  • KVStore: Empty domain-isolated store whose ACL initially authorizes domainAddress for writes.

Types 2

type KVStore

interface
  1type KVStore interface {
  2	// GetDomainAddress returns the domain address.
  3	//
  4	// Returns:
  5	//   - address: Address of the domain realm that owns this store.
  6	GetDomainAddress() address
  7
  8	// GetAllKeys returns all keys in this store.
  9	//
 10	// Returns:
 11	//   - []string: Keys currently stored by the implementation, including any namespace prefix it uses.
 12	//   - error: nil when keys are enumerated successfully; otherwise the implementation's retrieval error.
 13	GetAllKeys() ([]string, error)
 14
 15	// Has checks if a key exists.
 16	//
 17	// Parameters:
 18	//   - key: Logical key to test for presence in the store.
 19	//
 20	// Returns:
 21	//   - bool: true when key has a stored entry; false when no entry exists.
 22	Has(key string) bool
 23
 24	// Get retrieves a value by key.
 25	//
 26	// Parameters:
 27	//   - key: Logical key whose stored value should be returned.
 28	//
 29	// Returns:
 30	//   - any: Value stored under key, including an explicitly stored nil.
 31	//   - error: nil when key exists; otherwise the implementation's missing-key error.
 32	Get(key string) (any, error)
 33
 34	// GetInt64 retrieves an int64 value by key.
 35	//
 36	// Parameters:
 37	//   - key: Logical key whose value must have dynamic type int64.
 38	//
 39	// Returns:
 40	//   - int64: Stored int64 value, or the implementation's zero value when retrieval or casting fails.
 41	//   - error: nil on success; otherwise a missing-key or failed-cast error.
 42	GetInt64(key string) (int64, error)
 43
 44	// GetUint64 retrieves a uint64 value by key.
 45	//
 46	// Parameters:
 47	//   - key: Logical key whose value must have dynamic type uint64.
 48	//
 49	// Returns:
 50	//   - uint64: Stored uint64 value, or the implementation's zero value when retrieval or casting fails.
 51	//   - error: nil on success; otherwise a missing-key or failed-cast error.
 52	GetUint64(key string) (uint64, error)
 53
 54	// GetBool retrieves a bool value by key.
 55	//
 56	// Parameters:
 57	//   - key: Logical key whose value must have dynamic type bool.
 58	//
 59	// Returns:
 60	//   - bool: Stored bool value, or false when retrieval or casting fails.
 61	//   - error: nil on success; otherwise a missing-key or failed-cast error.
 62	GetBool(key string) (bool, error)
 63
 64	// GetString retrieves a string value by key.
 65	//
 66	// Parameters:
 67	//   - key: Logical key whose value must have dynamic type string.
 68	//
 69	// Returns:
 70	//   - string: Stored string value, or the empty string when retrieval or casting fails.
 71	//   - error: nil on success; otherwise a missing-key or failed-cast error.
 72	GetString(key string) (string, error)
 73
 74	// GetAddress retrieves an address value by key.
 75	//
 76	// Parameters:
 77	//   - key: Logical key whose value must have dynamic type address.
 78	//
 79	// Returns:
 80	//   - address: Stored address value, or the empty address when retrieval or casting fails.
 81	//   - error: nil on success; otherwise a missing-key or failed-cast error.
 82	GetAddress(key string) (address, error)
 83
 84	// GetBPTree retrieves a B+ tree value by key.
 85	//
 86	// Parameters:
 87	//   - key: Logical key whose value must have dynamic type *bptree.BPTree.
 88	//
 89	// Returns:
 90	//   - *bptree.BPTree: Stored B+ tree pointer, or nil when retrieval or casting fails.
 91	//   - error: nil on success; otherwise a missing-key or failed-cast error.
 92	GetBPTree(key string) (*bptree.BPTree, error)
 93
 94	// Set stores a value with the given key.
 95	//
 96	// Parameters:
 97	//   - _: Interrealm-call discriminator; callers pass 0.
 98	//   - rlm: Propagated current realm context from the domain wrapper; implementations validate it and may use its address for write authorization.
 99	//   - key: Logical key under which value is stored.
100	//   - value: Arbitrary value to associate with key.
101	//
102	// Returns:
103	//   - error: nil when value is stored; otherwise the implementation's realm, authorization, or storage error.
104	Set(_ int, rlm realm, key string, value any) error
105
106	// Delete removes a key.
107	//
108	// Parameters:
109	//   - _: Interrealm-call discriminator; callers pass 0.
110	//   - rlm: Propagated current realm context from the domain wrapper; implementations validate it and may use its address for write authorization.
111	//   - key: Logical key whose stored entry should be removed.
112	//
113	// Returns:
114	//   - error: nil when key is removed; otherwise the implementation's realm, authorization, missing-key, or storage error.
115	Delete(_ int, rlm realm, key string) error
116
117	// IsWriteAuthorized checks if the caller has write permission.
118	//
119	// Parameters:
120	//   - caller: Address whose write permission should be checked.
121	//
122	// Returns:
123	//   - bool: true when caller is authorized to write; false otherwise.
124	IsWriteAuthorized(caller address) bool
125
126	// GetAuthorizedCallers returns all authorized callers and their permissions.
127	//
128	// Returns:
129	//   - map[address]Permission: Caller-to-permission mapping exposed by the implementation.
130	//   - error: nil when the ACL is available; otherwise the implementation's ACL retrieval error.
131	GetAuthorizedCallers() (map[address]Permission, error)
132
133	// AddAuthorizedCaller adds a new authorized caller.
134	//
135	// Parameters:
136	//   - _: Interrealm-call discriminator; callers pass 0.
137	//   - rlm: Propagated current realm context from the domain wrapper; implementations validate it before changing ACL state.
138	//   - caller: Address to add to the authorization map.
139	//   - permission: Permission to assign to caller; the concrete store accepts Write.
140	//
141	// Returns:
142	//   - error: nil when caller is added; otherwise the implementation's realm, authorization, duplicate, permission, or storage error.
143	AddAuthorizedCaller(_ int, rlm realm, caller address, permission Permission) error
144
145	// UpdateAuthorizedCaller updates an existing caller's permission.
146	//
147	// Parameters:
148	//   - _: Interrealm-call discriminator; callers pass 0.
149	//   - rlm: Propagated current realm context from the domain wrapper; implementations validate it before changing ACL state.
150	//   - caller: Address of the registered caller to update.
151	//   - permission: Replacement permission for caller; the concrete store accepts Write.
152	//
153	// Returns:
154	//   - error: nil when caller's permission is updated; otherwise the implementation's realm, authorization, missing-caller, permission, or storage error.
155	UpdateAuthorizedCaller(_ int, rlm realm, caller address, permission Permission) error
156
157	// RemoveAuthorizedCaller removes an authorized caller.
158	//
159	// Parameters:
160	//   - _: Interrealm-call discriminator; callers pass 0.
161	//   - rlm: Propagated current realm context from the domain wrapper; implementations validate it before changing ACL state.
162	//   - caller: Address of the registered caller to remove.
163	//
164	// Returns:
165	//   - error: nil when caller is removed; otherwise the implementation's realm, authorization, missing-caller, or storage error.
166	RemoveAuthorizedCaller(_ int, rlm realm, caller address) error
167}
source

KVStore interface for domain-specific storage Each domain creates its own instance of KVStore

Imports 3

Source Files 6