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

store 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.

## Permission Model

There is exactly one assignable permission level:

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

The zero value of `Permission` is reserved and rejected by registration APIs. The domain address 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.Previous().Address()` (the caller) against the write ACL. Set additionally bypasses the ACL when `rlm.IsCode()` is false so EOA / user-realm callers (deploy, tests) can populate state without prior registration; Delete has no such bypass.
  • `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"

)

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

  • **Read Operations** (`Get`, `GetInt64`, `GetString`, ...): no permission check; reads are public within the package.
  • **Write Operations** (`Set`, `Delete`): require the caller -- `rlm.Previous().Address()` -- to be in the write ACL. Set additionally bypasses the ACL when the immediate caller is an EOA / user realm (`!rlm.IsCode()`); Delete has no such bypass.
  • **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

  • `ErrKeyNotFound` - Requested key does not exist.
  • `ErrWritePermissionDenied` - the caller (`rlm.Previous().Address()`) is not in the write ACL. For Set, this only fires when `rlm.IsCode()` is true; the EOA / user-realm path bypasses the ACL.
  • `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.
  • 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 domain: the name of the domain using this store (e.g., "pool", "position")

Types 2

type KVStore

interface
 1type KVStore interface {
 2	// GetDomainAddress returns the domain address
 3	GetDomainAddress() address
 4
 5	// GetAllKeys returns all keys in this store
 6	GetAllKeys() ([]string, error)
 7
 8	// Has checks if a key exists
 9	Has(key string) bool
10
11	// Get retrieves a value by key
12	Get(key string) (any, error)
13
14	// GetInt64 retrieves a int64 value by key
15	GetInt64(key string) (int64, error)
16
17	// GetUint64 retrieves a uint64 value by key
18	GetUint64(key string) (uint64, error)
19
20	// GetBool retrieves a bool value by key
21	GetBool(key string) (bool, error)
22
23	// GetString retrieves a string value by key
24	GetString(key string) (string, error)
25
26	// GetAddress retrieves an address value by key
27	GetAddress(key string) (address, error)
28
29	// GetBPTree retrieves a B+ tree value by key
30	GetBPTree(key string) (*bptree.BPTree, error)
31
32	// Set stores a value with the given key
33	Set(_ int, rlm realm, key string, value any) error
34
35	// Delete removes a key
36	Delete(_ int, rlm realm, key string) error
37
38	// IsWriteAuthorized checks if the caller has write permission
39	IsWriteAuthorized(caller address) bool
40
41	// GetAuthorizedCallers returns all authorized callers and their permissions
42	GetAuthorizedCallers() (map[address]Permission, error)
43
44	// AddAuthorizedCaller adds a new authorized caller
45	AddAuthorizedCaller(_ int, rlm realm, caller address, permission Permission) error
46
47	// UpdateAuthorizedCaller updates an existing caller's permission
48	UpdateAuthorizedCaller(_ int, rlm realm, caller address, permission Permission) error
49
50	// RemoveAuthorizedCaller removes an authorized caller
51	RemoveAuthorizedCaller(_ int, rlm realm, caller address) error
52}
source

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

Imports 3

Source Files 6