Transaction
A140F75A532940…FF312EC9AA1A
Block 77,253 · index 1 · indexed
Summary
- Hash
- A140F75A532940A1C50B53EE72751616D60693BC1C06300DC3ABFF312EC9AA1A
- Block
- 77,253
- Size
- 76667 bytes
- Gas used
- 99,267,691 / 119,121,182
- Fee
- 119122ugnot
- Status
- success
Messages
Arguments · 24
- #1protocol_fee
- #2README.md
- #3# Protocol Fee Fee collection and distribution for protocol operations. ## Overview The protocol-fee contract collects authorized fees from protocol operations and distributes them to GovStaker and DevOps according to configured percentages. ## Gnoweb The root `Render("")` delegates to the active implementation and shows realm identity, distribution allocations in basis points, recipient addresses, the accrual epoch, and halt flags. Rendering reads fixed configuration without aggregating balances across tokens. Unsupported paths return `404`. ## Configuration - **Router Fee (initial/default)**: 0.15% of the swap amount; configured by the router, with an admin-or-governance range of 0–10% - **Pool Creation Fee (initial/default)**: 100 GNS; configured by the pool and modifiable through governance - **Withdrawal Fee (initial/default)**: 1% of LP fees claimed; configured by the pool and modifiable through governance - **Unstaking Fee (initial/default)**: 1% of staking rewards; configured by the staker and modifiable by admin or governance - **Distribution (default)**: 100% to GovStaker and 0% to DevOps The operation-specific fees above are configured in their owning modules; they are not protocol-fee distribution percentages. ## Fee Sources 1. **Swaps**: The router applies its configured swap fee (0.15% initially). 2. **Pool Creation**: The pool charges its configured creation fee (100 GNS initially). 3. **LP Withdrawals**: The pool charges its configured withdrawal fee (1% initially). 4. **Staking Claims**: The staker charges its configured unstaking fee (1% initially). ## Key Functions ### `DistributeProtocolFee` Distributes accumulated fees to recipients. ### `SetDevOpsPct` Sets the DevOps funding percentage. ### `SetGovStakerPct` Sets the GovStaker funding percentage. ### `AddToProtocolFee` Adds an approved fee amount to the distribution queue. ### `AdvanceAccrualEpoch` Closes the accrual epoch in force and returns the new one. Called by gov/staker on every stake change, so fees are attributed to the stake distribution live when they arrived. ### `ConsumeAccrualBuckets` Returns and clears up to `limit` of the oldest pending buckets of one token, as parallel epoch and amount slices. Called by gov/staker when that token is collected. ## Usage ```go // Distribute accumulated fees DistributeProtocolFee(cross(cur)) // Configure distribution SetDevOpsPct(cross(cur), 2000) // 20% to DevOps SetGovStakerPct(cross(cur), 8000) // 80% to GovStaker // View tokens reserved for the next distribution GetReservedTokens() // View what gov/staker has not folded yet GetAccrualEpoch() GetAccrualPendingTokens() GetAccrualBuckets(tokenPath, 0) ``` ## Security - Configuration changes are restricted to admin or governance; distribution is restricted to admin or gov/staker - Automatic fee accumulation - Multi-token support - Transparent distribution tracking
- #4doc.gno
- #5// Package protocol_fee manages fee collection and distribution for GnoSwap protocol operations. // // This contract collects authorized fees from protocol operations such as swaps, // pool creation, withdrawals, and staking claims, then accounts for their // distribution to DevOps and GovStaker according to configurable percentages. // // Distribution Targets: // - DevOps: Development and operations fund (default 0%) // - GovStaker: Governance-staker destination (default 100%) // // Accounting: // - reservedTokens tracks token paths with collected fees awaiting distribution. // - Per-token allocation accumulators and distribution-history trees distinguish // amounts allocated from amounts actually transferred. // - GovStaker allocations are bucketed by token path and accrual epoch. // // Key Functions: // - DistributeProtocolFee: Distributes accumulated fees to recipients // - SetDevOpsPct/SetGovStakerPct: Configure distribution percentages // - AddToProtocolFee: Pulls an approved fee into the distribution queue // // The contract uses a version manager pattern for upgradeable implementations. package protocol_fee
- #6getter.gno
- #7package protocol_fee // GetDomainPath returns the domain path of the protocol fee contract. // // Returns: // - domainPath: protocol fee contract domain path func GetDomainPath() string { return domainPath } // GetVersionPackagePath returns the current implementation package path. // // Returns: // - packagePath: current implementation package path func GetVersionPackagePath() string { return versionManager.GetCurrentPackagePath() }
- #8getter_utils.gno
- #9package protocol_fee func cloneStringInt64Map(src map[string]int64) map[string]int64 { if src == nil { return nil } dst := make(map[string]int64, len(src)) for key, value := range src { dst[key] = value } return dst } func cloneStringSlice(src []string) []string { if src == nil { return nil } dst := make([]string, len(src)) copy(dst, src) return dst } func cloneInt64Slice(src []int64) []int64 { if src == nil { return nil } dst := make([]int64, len(src)) copy(dst, src) return dst }
- #10gnomod.toml
- #11module = "gno.land/r/gnoswap/protocol_fee" gno = "0.9"
- #12proxy.gno
- #13package protocol_fee // DistributeProtocolFee distributes accumulated protocol fees to DevOps and // GovStaker, subject to the implementation's caller authorization and halt checks. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // // Halt check: returns without distributing while the Withdraw halt scope is active. func DistributeProtocolFee(cur realm) { getImplementation().DistributeProtocolFee(0, cur) } // DistributeProtocolFeeByTokenPath distributes accumulated protocol fees reserved // for one token path; an unreserved path has nothing to distribute. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // - tokenPath: token contract path whose reserved fee balance should be distributed // // Halt check: returns without distributing while the Withdraw halt scope is active. func DistributeProtocolFeeByTokenPath(cur realm, tokenPath string) { getImplementation().DistributeProtocolFeeByTokenPath(0, cur, tokenPath) } // SetDevOpsPct sets the share of protocol fees allocated to DevOps. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // - pct: DevOps allocation in basis-point units, from 0 through 10000 inclusive (10000 = 100%) // // Halt check: reverts while the ProtocolFee halt scope is active. func SetDevOpsPct(cur realm, pct int64) { getImplementation().SetDevOpsPct(0, cur, pct) } // SetGovStakerPct sets the share of protocol fees allocated to GovStaker. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // - pct: GovStaker allocation in basis-point units, from 0 through 10000 inclusive (10000 = 100%) // // Halt check: reverts while the ProtocolFee halt scope is active. func SetGovStakerPct(cur realm, pct int64) { getImplementation().SetGovStakerPct(0, cur, pct) } // AddToProtocolFee pulls an approved fee amount from an authorized protocol caller // into protocol-fee accounting. Authorized callers are pool, position, router, // and staker realms; direct transfers to this address do not register a fee. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // - tokenPath: token contract path whose fee is being collected // - amount: non-negative fee amount in the token's base units; zero performs no transfer // // Returns: // - err: nil on success; errSpoofedRealm for a spoofed-realm call; errProtocolFeeHalted when protocol-fee collection is halted // // Halt check: returns errProtocolFeeHalted without transferring while the ProtocolFee halt scope is active. func AddToProtocolFee(cur realm, tokenPath string, amount int64) error { return getImplementation().AddToProtocolFee(0, cur, tokenPath, amount) } // AdvanceAccrualEpoch closes the current accrual epoch and returns the new one. Fees // arriving from now on are bucketed under the returned epoch. Only gov/staker may call // it, on every stake change. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // // Returns: // - epoch: newly active non-negative accrual epoch used for subsequently collected GovStaker fees func AdvanceAccrualEpoch(cur realm) int64 { return getImplementation().AdvanceAccrualEpoch(0, cur) } // ConsumeAccrualBuckets returns and clears up to limit of the oldest pending buckets // of tokenPath as parallel epoch and amount slices. A limit of zero or less consumes // every pending bucket. Only gov/staker may call it. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // - tokenPath: token contract path whose pending GovStaker accrual buckets are consumed // - limit: maximum number of oldest buckets to consume; zero or negative consumes all pending buckets // // Returns: // - epochs: consumed accrual epoch numbers in ascending epoch order // - amounts: GovStaker fee amounts corresponding positionally to epochs, in token base units func ConsumeAccrualBuckets(cur realm, tokenPath string, limit int) ([]int64, []int64) { return getImplementation().ConsumeAccrualBuckets(0, cur, tokenPath, limit) } // GetDevOpsPct returns the DevOps fee allocation in basis-point units. // // Returns: // - pct: configured DevOps allocation from 0 through 10000 (10000 = 100%) func GetDevOpsPct() int64 { return getImplementation().GetDevOpsPct() } // GetGovStakerPct returns the GovStaker fee allocation in basis-point units. // // Returns: // - pct: configured GovStaker allocation from 0 through 10000 (10000 = 100%) func GetGovStakerPct() int64 { return getImplementation().GetGovStakerPct() } // GetAccrualEpoch returns the accrual epoch fees are currently bucketed under. // // Returns: // - epoch: current non-negative accrual epoch assigned to newly collected GovStaker fees func GetAccrualEpoch() int64 { return getImplementation().GetAccrualEpoch() } // GetAccrualPendingTokens returns the token paths that still own pending accrual buckets. // // Returns: // - tokenPaths: token contract paths with at least one pending GovStaker accrual bucket func GetAccrualPendingTokens() []string { return cloneStringSlice(getImplementation().GetAccrualPendingTokens()) } // GetAccrualBuckets returns up to limit of the oldest pending buckets of tokenPath // without clearing them. A limit of zero or less returns every pending bucket. // // Parameters: // - tokenPath: token contract path whose pending GovStaker accrual buckets are queried // - limit: maximum number of oldest buckets to return; zero or negative returns all pending buckets // // Returns: // - epochs: selected accrual epoch numbers in ascending epoch order // - amounts: GovStaker fee amounts corresponding positionally to epochs, in token base units func GetAccrualBuckets(tokenPath string, limit int) ([]int64, []int64) { epochs, amounts := getImplementation().GetAccrualBuckets(tokenPath, limit) return cloneInt64Slice(epochs), cloneInt64Slice(amounts) } // GetReservedTokens returns token paths reserved for distribution. // // Returns: // - tokenPaths: token paths reserved for distribution func GetReservedTokens() []string { return cloneStringSlice(getImplementation().GetReservedTokens()) } // GetAccuTransfersToGovStaker returns accumulated transfers to GovStaker. // // Returns: // - transfers: map of token paths to accumulated amounts func GetAccuTransfersToGovStaker() map[string]int64 { return cloneStringInt64Map(getImplementation().GetAccuTransfersToGovStaker()) } // GetAccuTransfersToDevOps returns accumulated transfers to DevOps. // // Returns: // - transfers: map of token paths to accumulated amounts func GetAccuTransfersToDevOps() map[string]int64 { return cloneStringInt64Map(getImplementation().GetAccuTransfersToDevOps()) } // GetAccuTransferToGovStakerByTokenPath returns accumulated GovStaker transfer for a token. // // Parameters: // - tokenPath: path of the token // // Returns: // - amount: accumulated transfer amount func GetAccuTransferToGovStakerByTokenPath(tokenPath string) int64 { return getImplementation().GetAccuTransferToGovStakerByTokenPath(tokenPath) } // GetAccuTransferToDevOpsByTokenPath returns accumulated DevOps transfer for a token. // // Parameters: // - tokenPath: path of the token // // Returns: // - amount: accumulated transfer amount func GetAccuTransferToDevOpsByTokenPath(tokenPath string) int64 { return getImplementation().GetAccuTransferToDevOpsByTokenPath(tokenPath) } // GetActualDistributedToGovStaker returns actual transfers completed to GovStaker. // // Returns: // - transfers: map of token paths to actual distributed amounts func GetActualDistributedToGovStaker() map[string]int64 { return cloneStringInt64Map(getImplementation().GetActualDistributedToGovStaker()) } // GetActualDistributedToDevOps returns actual transfers completed to DevOps. // // Returns: // - transfers: map of token paths to actual distributed amounts func GetActualDistributedToDevOps() map[string]int64 { return cloneStringInt64Map(getImplementation().GetActualDistributedToDevOps()) } // GetActualDistributedToGovStakerByTokenPath returns actual GovStaker transfer for a token. // // Parameters: // - tokenPath: path of the token // // Returns: // - amount: actual distributed amount for the token func GetActualDistributedToGovStakerByTokenPath(tokenPath string) int64 { return getImplementation().GetActualDistributedToGovStakerByTokenPath(tokenPath) } // GetActualDistributedToDevOpsByTokenPath returns actual DevOps transfer for a token. // // Parameters: // - tokenPath: path of the token // // Returns: // - amount: actual distributed amount for the token func GetActualDistributedToDevOpsByTokenPath(tokenPath string) int64 { return getImplementation().GetActualDistributedToDevOpsByTokenPath(tokenPath) }
- #14render.gno
- #15package protocol_fee // Render delegates web rendering to the active implementation. func Render(path string) string { return getImplementation().Render(path) }
- #16state.gno
- #17package protocol_fee import ( "errors" _ "gno.land/r/gnoswap/rbac/v1" // initialize readable contract role(s) "gno.land/p/gnoswap/store/v1" "gno.land/p/gnoswap/version_manager/v1" ) var ( currentAddress address domainPath string kvStore store.KVStore versionManager version_manager.VersionManager implementation IProtocolFee ) func init(cur realm) { currentAddress = cur.Address() domainPath = cur.PkgPath() // Create a new KV store instance for this domain kvStore = store.NewKVStore(currentAddress) // Initialize the initializers map to store implementation registration functions versionManager = version_manager.NewVersionManager( domainPath, kvStore, initializeDomainStore, ) implementation = nil } func initializeDomainStore(_ int, rlm realm, kvStore store.KVStore) any { return NewProtocolFeeStore(kvStore) } func getImplementation() IProtocolFee { if implementation == nil { panic("implementation is not initialized") } return implementation } func updateImplementation() error { result := versionManager.GetCurrentImplementation() if result == nil { return errors.New("implementation is not initialized") } impl, ok := result.(IProtocolFee) if !ok { return errors.New("impl is not an IProtocolFee") } implementation = impl return nil }
- #18store.gno
- #19package protocol_fee import ( "errors" bptree "gno.land/p/nt/bptree/v0" ufmt "gno.land/p/nt/ufmt/v0" "gno.land/p/gnoswap/gnsmath/v1" "gno.land/p/gnoswap/store/v1" "gno.land/p/gnoswap/utils/v1" ) type StoreKey string // String returns the textual store-key value. // // Returns: // - key: The string representation of the StoreKey. func (s StoreKey) String() string { return string(s) } const ( // By default, devOps will get 0% of the protocol fee (which means gov/staker will get 100% of the protocol fee) // This percentage can be modified through governance. StoreKeyDevOpsPct StoreKey = "devOpsPct" // accuToGovStaker tracks the cumulative amount allocated to GovStaker, // including allocations that are still pending distribution. StoreKeyAccuToGovStaker StoreKey = "accuToGovStaker" // tokenPath -> amount // accuToDevOps tracks the cumulative amount allocated to DevOps, // including allocations that are still pending distribution. StoreKeyAccuToDevOps StoreKey = "accuToDevOps" // tokenPath -> amount // Distribution-history trees track cumulative amounts actually transferred. StoreKeyDistributedToGovStakerHistory StoreKey = "distributedToGovStakerHistory" // tokenPath -> amount StoreKeyDistributedToDevOpsHistory StoreKey = "distributedToDevOpsHistory" // tokenPath -> amount // reservedTokens tracks token paths collected but not yet distributed. StoreKeyReservedTokens StoreKey = "reservedTokens" // accrualEpoch, accrualBuckets and accrualPendingTokens track the gov/staker share // per token path and accrual epoch until gov/staker folds it into its accumulator. StoreKeyAccrualEpoch StoreKey = "accrualEpoch" StoreKeyAccrualBuckets StoreKey = "accrualBuckets" // tokenPath -> (epoch -> amount) StoreKeyAccrualPendingTokens StoreKey = "accrualPendingTokens" // tokenPath -> true ) const ( defaultDevOpsPct = int64(0) errSpoofedRealm = "rlm does not match the current crossing frame" ) // NewBPTreeN allocates a BP-tree under /r/gnoswap/protocol_fee's realm context // so tree.Set leaf-slot writes clear the readonly-taint gate regardless of // which realm (protocol_fee/v1, mock, tests) calls Set. Callers must allocate // protocol_fee trees through here rather than bptree.NewBPTreeN directly. // // Parameters: // - fanout: Branching factor passed to the BP-tree constructor. // // Returns: // - tree: A new BP-tree configured with fanout and allocated in the protocol-fee realm context. func NewBPTreeN(fanout int) *bptree.BPTree { return bptree.NewBPTreeN(fanout) } type protocolFeeStore struct { kvStore store.KVStore } // handle devOpsPct store data // // Returns: // - exists: True when the DevOps percentage store key is present in the KV store. func (s *protocolFeeStore) HasDevOpsPctStoreKey() bool { return s.kvStore.Has(StoreKeyDevOpsPct.String()) } // InitializeDevOpsPct creates the DevOps allocation percentage entry with its default value. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // // Returns: // - err: Nil when the key is initialized; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) InitializeDevOpsPct(_ int, rlm realm) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyDevOpsPct.String(), defaultDevOpsPct) } // GetDevOpsPct reads the persisted DevOps allocation percentage. // // Returns: // - pct: The stored DevOps allocation percentage in basis points; the method panics if the key is missing or cannot be decoded as int64. func (s *protocolFeeStore) GetDevOpsPct() int64 { devOpsPct, err := s.kvStore.GetInt64(StoreKeyDevOpsPct.String()) if err != nil { panic(err) } return devOpsPct } // SetDevOpsPct persists the DevOps allocation percentage. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // - pct: DevOps share of protocol fees in basis points; the state layer constrains this value to 0 through 10000 before calling the store. // // Returns: // - err: Nil when the percentage is persisted; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) SetDevOpsPct(_ int, rlm realm, pct int64) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyDevOpsPct.String(), pct) } // handle accuToGovStaker store data // // Returns: // - exists: True when the cumulative Gov/Staker allocation tree key is present in the KV store. func (s *protocolFeeStore) HasAccuToGovStakerStoreKey() bool { return s.kvStore.Has(StoreKeyAccuToGovStaker.String()) } // InitializeAccuToGovStaker creates the cumulative Gov/Staker allocation tree. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // // Returns: // - err: Nil when the tree is initialized; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) InitializeAccuToGovStaker(_ int, rlm realm) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyAccuToGovStaker.String(), NewBPTreeN(16)) } // GetAccuToGovStaker reads the cumulative Gov/Staker allocation tree. // // Returns: // - tree: The BP-tree mapping token paths to cumulative amounts; the method panics if the store value is absent or has the wrong type. func (s *protocolFeeStore) GetAccuToGovStaker() *bptree.BPTree { accuToGovStaker, err := s.kvStore.GetBPTree(StoreKeyAccuToGovStaker.String()) if err != nil { panic(err) } return accuToGovStaker } // GetAccuToGovStakerItem reads one token's cumulative Gov/Staker allocation. // // Parameters: // - tokenPath: Token path whose cumulative allocation should be looked up. // // Returns: // - amount: The stored cumulative allocation for tokenPath, or zero when no entry exists. // - found: True when tokenPath has an entry in the allocation tree; false when it is absent. func (s *protocolFeeStore) GetAccuToGovStakerItem(tokenPath string) (int64, bool) { accuToGovStaker, err := s.kvStore.GetBPTree(StoreKeyAccuToGovStaker.String()) if err != nil { panic(err) } result := accuToGovStaker.Get(tokenPath) if result == nil { return 0, false } amount, ok := result.(int64) if !ok { panic(ufmt.Errorf("failed to cast result to int64: %T", result)) } return amount, true } // SetAccuToGovStakerItem updates one token's cumulative Gov/Staker allocation. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // - tokenPath: Token path whose cumulative allocation is replaced. // - amount: Cumulative Gov/Staker allocation to store for tokenPath. // // Returns: // - err: Nil when the allocation tree is persisted; otherwise the current-realm, tree-read, or KV-store write error. func (s *protocolFeeStore) SetAccuToGovStakerItem(_ int, rlm realm, tokenPath string, amount int64) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } accuToGovStaker, err := s.kvStore.GetBPTree(StoreKeyAccuToGovStaker.String()) if err != nil { return err } accuToGovStaker.Set(tokenPath, amount) return s.kvStore.Set(0, rlm, StoreKeyAccuToGovStaker.String(), accuToGovStaker) } // handle accuToDevOps store data // // Returns: // - exists: True when the cumulative DevOps allocation tree key is present in the KV store. func (s *protocolFeeStore) HasAccuToDevOpsStoreKey() bool { return s.kvStore.Has(StoreKeyAccuToDevOps.String()) } // InitializeAccuToDevOps creates the cumulative DevOps allocation tree. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // // Returns: // - err: Nil when the tree is initialized; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) InitializeAccuToDevOps(_ int, rlm realm) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyAccuToDevOps.String(), NewBPTreeN(16)) } // GetAccuToDevOps reads the cumulative DevOps allocation tree. // // Returns: // - tree: The BP-tree mapping token paths to cumulative amounts; the method panics if the store value is absent or has the wrong type. func (s *protocolFeeStore) GetAccuToDevOps() *bptree.BPTree { accuToDevOps, err := s.kvStore.GetBPTree(StoreKeyAccuToDevOps.String()) if err != nil { panic(err) } return accuToDevOps } // GetAccuToDevOpsItem reads one token's cumulative DevOps allocation. // // Parameters: // - tokenPath: Token path whose cumulative allocation should be looked up. // // Returns: // - amount: The stored cumulative allocation for tokenPath, or zero when no entry exists. // - found: True when tokenPath has an entry in the allocation tree; false when it is absent. func (s *protocolFeeStore) GetAccuToDevOpsItem(tokenPath string) (int64, bool) { accuToDevOps, err := s.kvStore.GetBPTree(StoreKeyAccuToDevOps.String()) if err != nil { panic(err) } result := accuToDevOps.Get(tokenPath) if result == nil { return 0, false } amount, ok := result.(int64) if !ok { panic(ufmt.Errorf("failed to cast result to int64: %T", result)) } return amount, true } // SetAccuToDevOpsItem updates one token's cumulative DevOps allocation. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // - tokenPath: Token path whose cumulative allocation is replaced. // - amount: Cumulative DevOps allocation to store for tokenPath. // // Returns: // - err: Nil when the allocation tree is persisted; otherwise the current-realm, tree-read, or KV-store write error. func (s *protocolFeeStore) SetAccuToDevOpsItem(_ int, rlm realm, tokenPath string, amount int64) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } accuToDevOps, err := s.kvStore.GetBPTree(StoreKeyAccuToDevOps.String()) if err != nil { return err } accuToDevOps.Set(tokenPath, amount) return s.kvStore.Set(0, rlm, StoreKeyAccuToDevOps.String(), accuToDevOps) } // handle distributedToGovStakerHistory store data // HasDistributedToGovStakerHistoryStoreKey reports whether the Gov/Staker distribution-history tree exists. // // Returns: // - exists: True when the Gov/Staker distribution-history tree key is present in the KV store. func (s *protocolFeeStore) HasDistributedToGovStakerHistoryStoreKey() bool { return s.kvStore.Has(StoreKeyDistributedToGovStakerHistory.String()) } // InitializeDistributedToGovStakerHistory creates the Gov/Staker distribution-history tree. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // // Returns: // - err: Nil when the history tree is initialized; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) InitializeDistributedToGovStakerHistory(_ int, rlm realm) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyDistributedToGovStakerHistory.String(), NewBPTreeN(16)) } // GetDistributedToGovStakerHistory reads the cumulative Gov/Staker distribution-history tree. // // Returns: // - tree: The BP-tree mapping token paths to amounts actually distributed to Gov/Staker; the method panics if the stored value is absent or has the wrong type. func (s *protocolFeeStore) GetDistributedToGovStakerHistory() *bptree.BPTree { distributedToGovStakerHistory, err := s.kvStore.GetBPTree(StoreKeyDistributedToGovStakerHistory.String()) if err != nil { panic(err) } return distributedToGovStakerHistory } // GetDistributedToGovStakerHistoryItem reads one token's cumulative Gov/Staker distribution history. // // Parameters: // - tokenPath: Token path whose distributed amount should be looked up. // // Returns: // - amount: The stored cumulative amount distributed to Gov/Staker for tokenPath, or zero when no entry exists. // - found: True when tokenPath has a history entry; false when it is absent. func (s *protocolFeeStore) GetDistributedToGovStakerHistoryItem(tokenPath string) (int64, bool) { distributedToGovStakerHistory, err := s.kvStore.GetBPTree(StoreKeyDistributedToGovStakerHistory.String()) if err != nil { panic(err) } result := distributedToGovStakerHistory.Get(tokenPath) if result == nil { return 0, false } amount, ok := result.(int64) if !ok { panic(ufmt.Errorf("failed to cast result to int64: %T", result)) } return amount, true } // SetDistributedToGovStakerHistoryItem updates one token's cumulative Gov/Staker distribution history. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // - tokenPath: Token path whose distributed amount is replaced. // - amount: Cumulative amount actually distributed to Gov/Staker to store for tokenPath. // // Returns: // - err: Nil when the history tree is persisted; otherwise the current-realm, tree-read, or KV-store write error. func (s *protocolFeeStore) SetDistributedToGovStakerHistoryItem(_ int, rlm realm, tokenPath string, amount int64) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } distributedToGovStakerHistory, err := s.kvStore.GetBPTree(StoreKeyDistributedToGovStakerHistory.String()) if err != nil { return err } distributedToGovStakerHistory.Set(tokenPath, amount) return s.kvStore.Set(0, rlm, StoreKeyDistributedToGovStakerHistory.String(), distributedToGovStakerHistory) } // HasDistributedToDevOpsHistoryStoreKey reports whether the DevOps distribution-history tree exists. // // Returns: // - exists: True when the DevOps distribution-history tree key is present in the KV store. // // handle distributedToDevOpsHistory store data func (s *protocolFeeStore) HasDistributedToDevOpsHistoryStoreKey() bool { return s.kvStore.Has(StoreKeyDistributedToDevOpsHistory.String()) } // InitializeDistributedToDevOpsHistory creates the DevOps distribution-history tree. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // // Returns: // - err: Nil when the history tree is initialized; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) InitializeDistributedToDevOpsHistory(_ int, rlm realm) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyDistributedToDevOpsHistory.String(), NewBPTreeN(16)) } // GetDistributedToDevOpsHistory reads the cumulative DevOps distribution-history tree. // // Returns: // - tree: The BP-tree mapping token paths to amounts actually distributed to DevOps; the method panics if the stored value is absent or has the wrong type. func (s *protocolFeeStore) GetDistributedToDevOpsHistory() *bptree.BPTree { distributedToDevOpsHistory, err := s.kvStore.GetBPTree(StoreKeyDistributedToDevOpsHistory.String()) if err != nil { panic(err) } return distributedToDevOpsHistory } // GetDistributedToDevOpsHistoryItem reads one token's cumulative DevOps distribution history. // // Parameters: // - tokenPath: Token path whose distributed amount should be looked up. // // Returns: // - amount: The stored cumulative amount distributed to DevOps for tokenPath, or zero when no entry exists. // - found: True when tokenPath has a history entry; false when it is absent. func (s *protocolFeeStore) GetDistributedToDevOpsHistoryItem(tokenPath string) (int64, bool) { distributedToDevOpsHistory, err := s.kvStore.GetBPTree(StoreKeyDistributedToDevOpsHistory.String()) if err != nil { panic(err) } result := distributedToDevOpsHistory.Get(tokenPath) if result == nil { return 0, false } amount, ok := result.(int64) if !ok { panic(ufmt.Errorf("failed to cast result to int64: %T", result)) } return amount, true } // SetDistributedToDevOpsHistoryItem updates one token's cumulative DevOps distribution history. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // - tokenPath: Token path whose distributed amount is replaced. // - amount: Cumulative amount actually distributed to DevOps to store for tokenPath. // // Returns: // - err: Nil when the history tree is persisted; otherwise the current-realm, tree-read, or KV-store write error. func (s *protocolFeeStore) SetDistributedToDevOpsHistoryItem(_ int, rlm realm, tokenPath string, amount int64) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } distributedToDevOpsHistory, err := s.kvStore.GetBPTree(StoreKeyDistributedToDevOpsHistory.String()) if err != nil { return err } distributedToDevOpsHistory.Set(tokenPath, amount) return s.kvStore.Set(0, rlm, StoreKeyDistributedToDevOpsHistory.String(), distributedToDevOpsHistory) } // handle reservedTokens store data // // reservedTokens is the set of token paths that collected a fee not yet transferred out // by DistributeProtocolFee. It is kept as a tree so that a single token can be added or // settled without rewriting the whole set. // // Returns: // - exists: True when the reserved-token index key is present in the KV store. func (s *protocolFeeStore) HasReservedTokensStoreKey() bool { return s.kvStore.Has(StoreKeyReservedTokens.String()) } // InitializeReservedTokens creates the reserved-token index tree. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // // Returns: // - err: Nil when the index tree is initialized; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) InitializeReservedTokens(_ int, rlm realm) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyReservedTokens.String(), NewBPTreeN(16)) } func (s *protocolFeeStore) getReservedTokenTree() *bptree.BPTree { reservedTokens, err := s.kvStore.GetBPTree(StoreKeyReservedTokens.String()) if err != nil { panic(err) } return reservedTokens } // GetReservedTokens returns all token paths currently reserved for distribution. // // Returns: // - tokenPaths: Token paths present in the reserved-token index, in BP-tree iteration order. func (s *protocolFeeStore) GetReservedTokens() []string { return collectTreeKeys(s.getReservedTokenTree()) } // HasReservedToken reports whether tokenPath is currently reserved for distribution. // // Parameters: // - tokenPath: Token path to look up in the reserved-token index. // // Returns: // - exists: True when tokenPath is in the reserved-token index; false otherwise. func (s *protocolFeeStore) HasReservedToken(tokenPath string) bool { return s.getReservedTokenTree().Has(tokenPath) } // AddReservedToken adds tokenPath to the reserved-token index. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // - tokenPath: Token path collected for protocol-fee distribution. // // Returns: // - err: Nil when tokenPath is already present or has been added; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) AddReservedToken(_ int, rlm realm, tokenPath string) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } reservedTokens := s.getReservedTokenTree() if reservedTokens.Has(tokenPath) { return nil } reservedTokens.Set(tokenPath, true) return s.kvStore.Set(0, rlm, StoreKeyReservedTokens.String(), reservedTokens) } // RemoveReservedToken removes one tokenPath from the reserved-token set when present. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // - tokenPath: Token path to remove from the reserved-token set. // // Returns: // - err: Nil when tokenPath is absent or has been removed; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) RemoveReservedToken(_ int, rlm realm, tokenPath string) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } reservedTokens := s.getReservedTokenTree() if _, removed := reservedTokens.Remove(tokenPath); !removed { return nil } return s.kvStore.Set(0, rlm, StoreKeyReservedTokens.String(), reservedTokens) } // handle accrualEpoch store data // // accrualEpoch numbers the intervals between gov/staker stake changes. gov/staker // advances it on every stake change, so every fee that arrives is attributed to the // stake distribution in force when it arrived. // // Returns: // - exists: True when the accrual-epoch store key is present in the KV store. func (s *protocolFeeStore) HasAccrualEpochStoreKey() bool { return s.kvStore.Has(StoreKeyAccrualEpoch.String()) } // InitializeAccrualEpoch initializes the current fee-accrual epoch to zero. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // // Returns: // - err: Nil when the epoch key is initialized; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) InitializeAccrualEpoch(_ int, rlm realm) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyAccrualEpoch.String(), int64(0)) } // GetAccrualEpoch reads the epoch under which new Gov/Staker accrual buckets are recorded. // // Returns: // - epoch: The persisted current accrual epoch; the method panics if the key is missing or cannot be decoded as int64. func (s *protocolFeeStore) GetAccrualEpoch() int64 { accrualEpoch, err := s.kvStore.GetInt64(StoreKeyAccrualEpoch.String()) if err != nil { panic(err) } return accrualEpoch } // SetAccrualEpoch persists the current fee-accrual epoch. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // - accrualEpoch: Epoch number assigned to newly collected Gov/Staker accrual buckets. // // Returns: // - err: Nil when the epoch is persisted; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) SetAccrualEpoch(_ int, rlm realm, accrualEpoch int64) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyAccrualEpoch.String(), accrualEpoch) } // handle accrualBuckets store data // // accrualBuckets holds, per token path, the gov/staker share collected during each // accrual epoch that gov/staker has not folded into its accumulator yet. Folding // consumes the oldest buckets first, so a token is settled in epoch order. // // Returns: // - exists: True when the accrual-bucket store key is present in the KV store. func (s *protocolFeeStore) HasAccrualBucketsStoreKey() bool { return s.kvStore.Has(StoreKeyAccrualBuckets.String()) } // InitializeAccrualBuckets creates the nested accrual-bucket tree. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // // Returns: // - err: Nil when the bucket tree is initialized; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) InitializeAccrualBuckets(_ int, rlm realm) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyAccrualBuckets.String(), NewBPTreeN(16)) } func (s *protocolFeeStore) getAccrualBucketTree() *bptree.BPTree { accrualBuckets, err := s.kvStore.GetBPTree(StoreKeyAccrualBuckets.String()) if err != nil { panic(err) } return accrualBuckets } func (s *protocolFeeStore) getTokenAccrualBucketTree(accrualBuckets *bptree.BPTree, tokenPath string) *bptree.BPTree { result := accrualBuckets.Get(tokenPath) if result == nil { return nil } tokenBuckets, ok := result.(*bptree.BPTree) if !ok { panic(ufmt.Errorf("failed to cast result to *bptree.BPTree: %T", result)) } return tokenBuckets } // GetAccrualBuckets returns up to limit of the oldest pending buckets of tokenPath in // epoch order. A limit of zero or less returns every pending bucket. // // Parameters: // - tokenPath: Token path whose pending Gov/Staker accrual buckets should be read. // - limit: Maximum number of buckets to return; zero or a negative value returns all pending buckets. // // Returns: // - epochs: Epoch numbers for the returned buckets, in ascending BP-tree iteration order. // - amounts: Gov/Staker amounts for the returned epochs; amounts[i] corresponds to epochs[i]. func (s *protocolFeeStore) GetAccrualBuckets(tokenPath string, limit int) ([]int64, []int64) { epochs := []int64{} amounts := []int64{} tokenBuckets := s.getTokenAccrualBucketTree(s.getAccrualBucketTree(), tokenPath) if tokenBuckets == nil { return epochs, amounts } tokenBuckets.Iterate("", "", func(key string, value any) bool { if limit > 0 && len(epochs) >= limit { return true } amount, ok := value.(int64) if !ok { panic(ufmt.Errorf("failed to cast result to int64: %T", value)) } epochs = append(epochs, decodeEpochKey(key)) amounts = append(amounts, amount) return false }) return epochs, amounts } // AddAccrualBucket adds amount to the bucket of tokenPath at epoch. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // - tokenPath: Token path whose Gov/Staker accrual bucket is updated. // - epoch: Non-negative accrual epoch identifying the bucket; negative epochs panic during key encoding. // - amount: Amount to add to the existing bucket value. // // Returns: // - err: Nil when the bucket tree is persisted; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) AddAccrualBucket(_ int, rlm realm, tokenPath string, epoch int64, amount int64) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } accrualBuckets := s.getAccrualBucketTree() tokenBuckets := s.getTokenAccrualBucketTree(accrualBuckets, tokenPath) if tokenBuckets == nil { tokenBuckets = NewBPTreeN(16) accrualBuckets.Set(tokenPath, tokenBuckets) } key := encodeEpochKey(epoch) existing := int64(0) if result := tokenBuckets.Get(key); result != nil { current, ok := result.(int64) if !ok { panic(ufmt.Errorf("failed to cast result to int64: %T", result)) } existing = current } tokenBuckets.Set(key, gnsmath.SafeAddInt64(existing, amount)) return s.kvStore.Set(0, rlm, StoreKeyAccrualBuckets.String(), accrualBuckets) } // RemoveAccrualBuckets drops the buckets of tokenPath at the given epochs. The token's // tree is dropped as well once no bucket remains. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // - tokenPath: Token path whose selected accrual buckets are removed. // - epochs: Epoch numbers whose buckets should be deleted; an absent token or epoch is ignored. // // Returns: // - err: Nil when removals are applied (including an absent token); otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) RemoveAccrualBuckets(_ int, rlm realm, tokenPath string, epochs []int64) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } accrualBuckets := s.getAccrualBucketTree() tokenBuckets := s.getTokenAccrualBucketTree(accrualBuckets, tokenPath) if tokenBuckets == nil { return nil } for _, epoch := range epochs { tokenBuckets.Remove(encodeEpochKey(epoch)) } if tokenBuckets.Size() == 0 { accrualBuckets.Remove(tokenPath) } return s.kvStore.Set(0, rlm, StoreKeyAccrualBuckets.String(), accrualBuckets) } // handle accrualPendingTokens store data // // accrualPendingTokens is the set of token paths that still own at least one accrual // bucket, so gov/staker can enumerate what it has to fold without scanning every token. // // Returns: // - exists: True when the pending-token index key is present in the KV store. func (s *protocolFeeStore) HasAccrualPendingTokensStoreKey() bool { return s.kvStore.Has(StoreKeyAccrualPendingTokens.String()) } // InitializeAccrualPendingTokens creates the index of tokens with pending accrual buckets. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // // Returns: // - err: Nil when the pending-token index is initialized; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) InitializeAccrualPendingTokens(_ int, rlm realm) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } return s.kvStore.Set(0, rlm, StoreKeyAccrualPendingTokens.String(), NewBPTreeN(16)) } func (s *protocolFeeStore) getAccrualPendingTokenTree() *bptree.BPTree { accrualPendingTokens, err := s.kvStore.GetBPTree(StoreKeyAccrualPendingTokens.String()) if err != nil { panic(err) } return accrualPendingTokens } // GetAccrualPendingTokens returns token paths that still own accrual buckets. // // Returns: // - tokenPaths: Token paths in the pending-accrual index, in BP-tree iteration order. func (s *protocolFeeStore) GetAccrualPendingTokens() []string { return collectTreeKeys(s.getAccrualPendingTokenTree()) } // AddAccrualPendingToken adds tokenPath to the pending-accrual index. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // - tokenPath: Token path with at least one accrual bucket pending. // // Returns: // - err: Nil when tokenPath is already present or has been added; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) AddAccrualPendingToken(_ int, rlm realm, tokenPath string) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } accrualPendingTokens := s.getAccrualPendingTokenTree() if accrualPendingTokens.Has(tokenPath) { return nil } accrualPendingTokens.Set(tokenPath, true) return s.kvStore.Set(0, rlm, StoreKeyAccrualPendingTokens.String(), accrualPendingTokens) } // RemoveAccrualPendingToken drops one token from the set. It writes only when the // token was listed. // // Parameters: // - _: Crossing discriminator for the store operation; pass 0. // - rlm: Propagated realm context for this store write; it must be current (`rlm.IsCurrent()`). // - tokenPath: Token path to remove from the pending-accrual index. // // Returns: // - err: Nil when tokenPath is absent or has been removed; otherwise the current-realm or KV-store write error. func (s *protocolFeeStore) RemoveAccrualPendingToken(_ int, rlm realm, tokenPath string) error { if !rlm.IsCurrent() { return errors.New(errSpoofedRealm) } accrualPendingTokens := s.getAccrualPendingTokenTree() if _, removed := accrualPendingTokens.Remove(tokenPath); !removed { return nil } return s.kvStore.Set(0, rlm, StoreKeyAccrualPendingTokens.String(), accrualPendingTokens) } func collectTreeKeys(tree *bptree.BPTree) []string { keys := make([]string, 0, tree.Size()) tree.Iterate("", "", func(key string, _ any) bool { keys = append(keys, key) return false }) return keys } func encodeEpochKey(epoch int64) string { if epoch < 0 { panic(ufmt.Sprintf("negative epoch not supported: %d", epoch)) } return utils.EncodeUint64(uint64(epoch)) } func decodeEpochKey(key string) int64 { return gnsmath.SafeUint64ToInt64(utils.DecodeUint64(key)) } // NewProtocolFeeStore creates a protocol-fee store backed by the provided KV store. // This function is used by the upgrade system to create storage instances for each implementation. // // Parameters: // - kvStore: Domain KV store used to persist protocol-fee state. // // Returns: // - protocolFeeStore: An IProtocolFeeStore implementation backed by kvStore. func NewProtocolFeeStore(kvStore store.KVStore) IProtocolFeeStore { return &protocolFeeStore{ kvStore: kvStore, } }
- #20types.gno
- #21package protocol_fee import bptree "gno.land/p/nt/bptree/v0" type IProtocolFee interface { IProtocolFeeManager IProtocolFeeGetter Render(path string) string } type IProtocolFeeManager interface { // Mutating methods take `_ int, rlm realm` so a leading integer discriminator // (callers pass 0) and the current protocol_fee realm context are threaded // from proxy entry points down to cross-realm token transfers and store writes // performed inside each implementation. The discriminator is explicit at every // call site, making the realm threading visible to readers. // DistributeProtocolFee distributes all reserved protocol fees to DevOps and // GovStaker according to the configured allocation percentages. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context; implementation validates it as current DistributeProtocolFee(_ int, rlm realm) // DistributeProtocolFeeByTokenPath distributes the reserved fee for one token path. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context; implementation validates it as current // - tokenPath: token contract path whose reserved fee should be distributed DistributeProtocolFeeByTokenPath(_ int, rlm realm, tokenPath string) // SetDevOpsPct updates the DevOps share; the GovStaker share becomes 10000 minus pct. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context; implementation validates it as current // - pct: DevOps allocation in basis-point units, from 0 through 10000 inclusive SetDevOpsPct(_ int, rlm realm, pct int64) // SetGovStakerPct updates the GovStaker share; the DevOps share becomes 10000 minus pct. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context; implementation validates it as current // - pct: GovStaker allocation in basis-point units, from 0 through 10000 inclusive SetGovStakerPct(_ int, rlm realm, pct int64) // AddToProtocolFee records and pulls an approved fee from an authorized protocol caller. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context; implementation rejects a non-current value // - tokenPath: token contract path from which the approved fee is pulled // - amount: non-negative fee amount in token base units; zero is a successful no-op // // Returns: // - err: nil after accounting and transfer succeed; an error when realm validation, halt checks, or storage/transfer operations fail AddToProtocolFee(_ int, rlm realm, tokenPath string, amount int64) error // AdvanceAccrualEpoch closes the current epoch and selects the next epoch for new fees. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context; implementation validates it as current // // Returns: // - epoch: newly stored non-negative accrual epoch AdvanceAccrualEpoch(_ int, rlm realm) int64 // ConsumeAccrualBuckets removes pending buckets for one token in epoch order. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context; implementation validates it as current // - tokenPath: token contract path whose pending GovStaker buckets are consumed // - limit: maximum number of oldest buckets; zero or negative consumes all pending buckets // // Returns: // - epochs: consumed epoch numbers in ascending order // - amounts: fee amounts corresponding positionally to epochs, in token base units ConsumeAccrualBuckets(_ int, rlm realm, tokenPath string, limit int) ([]int64, []int64) } type IProtocolFeeGetter interface { // GetDevOpsPct returns the configured DevOps allocation. // // Returns: // - pct: DevOps fee allocation in basis-point units, from 0 through 10000 GetDevOpsPct() int64 // GetGovStakerPct returns the configured GovStaker allocation. // // Returns: // - pct: GovStaker fee allocation in basis-point units, from 0 through 10000 GetGovStakerPct() int64 // GetReservedTokens returns token paths whose collected fees await distribution. // // Returns: // - tokenPaths: token contract paths in the reserved-token index GetReservedTokens() []string // GetAccuTransfersToGovStaker returns cumulative allocations assigned to GovStaker. // // Returns: // - transfers: map from token contract path to cumulative allocated amount in base units GetAccuTransfersToGovStaker() map[string]int64 // GetAccuTransfersToDevOps returns cumulative allocations assigned to DevOps. // // Returns: // - transfers: map from token contract path to cumulative allocated amount in base units GetAccuTransfersToDevOps() map[string]int64 // GetAccuTransferToGovStakerByTokenPath returns the GovStaker allocation for one token. // // Parameters: // - path: token contract path whose cumulative GovStaker allocation is queried // // Returns: // - amount: cumulative GovStaker allocation for path in token base units, or zero if absent GetAccuTransferToGovStakerByTokenPath(path string) int64 // GetAccuTransferToDevOpsByTokenPath returns the DevOps allocation for one token. // // Parameters: // - path: token contract path whose cumulative DevOps allocation is queried // // Returns: // - amount: cumulative DevOps allocation for path in token base units, or zero if absent GetAccuTransferToDevOpsByTokenPath(path string) int64 // GetActualDistributedToGovStaker returns cumulative amounts transferred to GovStaker. // // Returns: // - transfers: map from token contract path to cumulative amount actually transferred in base units GetActualDistributedToGovStaker() map[string]int64 // GetActualDistributedToDevOps returns cumulative amounts transferred to DevOps. // // Returns: // - transfers: map from token contract path to cumulative amount actually transferred in base units GetActualDistributedToDevOps() map[string]int64 // GetActualDistributedToGovStakerByTokenPath returns the completed GovStaker transfer for one token. // // Parameters: // - path: token contract path whose completed GovStaker transfer is queried // // Returns: // - amount: cumulative GovStaker amount actually transferred for path in base units, or zero if absent GetActualDistributedToGovStakerByTokenPath(path string) int64 // GetActualDistributedToDevOpsByTokenPath returns the completed DevOps transfer for one token. // // Parameters: // - path: token contract path whose completed DevOps transfer is queried // // Returns: // - amount: cumulative DevOps amount actually transferred for path in base units, or zero if absent GetActualDistributedToDevOpsByTokenPath(path string) int64 // GetAccrualEpoch returns the epoch assigned to newly collected GovStaker fees. // // Returns: // - epoch: current non-negative accrual epoch GetAccrualEpoch() int64 // GetAccrualPendingTokens returns token paths that still have pending accrual buckets. // // Returns: // - tokenPaths: token contract paths with at least one pending GovStaker accrual bucket GetAccrualPendingTokens() []string // GetAccrualBuckets reads pending token buckets without removing them. // // Parameters: // - tokenPath: token contract path whose GovStaker accrual buckets are queried // - limit: maximum number of oldest buckets; zero or negative returns all pending buckets // // Returns: // - epochs: selected accrual epoch numbers in ascending order // - amounts: fee amounts corresponding positionally to epochs, in token base units GetAccrualBuckets(tokenPath string, limit int) ([]int64, []int64) } type IProtocolFeeStore interface { // HasDevOpsPctStoreKey reports whether the persisted DevOps percentage key exists. // // Returns: // - exists: true when the DevOps percentage key is present in the KV store HasDevOpsPctStoreKey() bool // InitializeDevOpsPct creates the DevOps percentage key with its default value. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // // Returns: // - err: nil when initialization succeeds; an error for invalid realm context or KV-store failure InitializeDevOpsPct(_ int, rlm realm) error // GetDevOpsPct reads the persisted DevOps percentage. // // Returns: // - pct: stored DevOps allocation in basis-point units GetDevOpsPct() int64 // SetDevOpsPct persists a new DevOps percentage. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // - pct: DevOps allocation in basis-point units // // Returns: // - err: nil when the value is stored; an error for invalid realm context or KV-store failure SetDevOpsPct(_ int, rlm realm, pct int64) error // HasAccuToGovStakerStoreKey reports whether the GovStaker allocation tree key exists. // // Returns: // - exists: true when the cumulative GovStaker allocation tree is present in the KV store HasAccuToGovStakerStoreKey() bool // InitializeAccuToGovStaker creates the cumulative GovStaker allocation tree. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // // Returns: // - err: nil when initialization succeeds; an error for invalid realm context or KV-store failure InitializeAccuToGovStaker(_ int, rlm realm) error // GetAccuToGovStaker returns the tree of cumulative GovStaker allocations by token. // // Returns: // - tree: persistent BPTree keyed by token path with int64 allocation values GetAccuToGovStaker() *bptree.BPTree // GetAccuToGovStakerItem reads one token's cumulative GovStaker allocation. // // Parameters: // - tokenPath: token contract path used as the allocation-tree key // // Returns: // - amount: stored allocation in token base units, or zero when tokenPath is absent // - exists: true when tokenPath has a stored allocation; false when absent GetAccuToGovStakerItem(tokenPath string) (int64, bool) // SetAccuToGovStakerItem stores one token's cumulative GovStaker allocation. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // - tokenPath: token contract path used as the allocation-tree key // - amount: cumulative GovStaker allocation in token base units to store // // Returns: // - err: nil when the value is stored; an error for invalid realm context, missing tree, or KV-store failure SetAccuToGovStakerItem(_ int, rlm realm, tokenPath string, amount int64) error // HasAccuToDevOpsStoreKey reports whether the DevOps allocation tree key exists. // // Returns: // - exists: true when the cumulative DevOps allocation tree is present in the KV store HasAccuToDevOpsStoreKey() bool // InitializeAccuToDevOps creates the cumulative DevOps allocation tree. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // // Returns: // - err: nil when initialization succeeds; an error for invalid realm context or KV-store failure InitializeAccuToDevOps(_ int, rlm realm) error // GetAccuToDevOps returns the tree of cumulative DevOps allocations by token. // // Returns: // - tree: persistent BPTree keyed by token path with int64 allocation values GetAccuToDevOps() *bptree.BPTree // GetAccuToDevOpsItem reads one token's cumulative DevOps allocation. // // Parameters: // - tokenPath: token contract path used as the allocation-tree key // // Returns: // - amount: stored allocation in token base units, or zero when tokenPath is absent // - exists: true when tokenPath has a stored allocation; false when absent GetAccuToDevOpsItem(tokenPath string) (int64, bool) // SetAccuToDevOpsItem stores one token's cumulative DevOps allocation. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // - tokenPath: token contract path used as the allocation-tree key // - amount: cumulative DevOps allocation in token base units to store // // Returns: // - err: nil when the value is stored; an error for invalid realm context, missing tree, or KV-store failure SetAccuToDevOpsItem(_ int, rlm realm, tokenPath string, amount int64) error // HasDistributedToGovStakerHistoryStoreKey reports whether the GovStaker history tree key exists. // // Returns: // - exists: true when the cumulative GovStaker distribution-history tree is present HasDistributedToGovStakerHistoryStoreKey() bool // InitializeDistributedToGovStakerHistory creates the GovStaker distribution-history tree. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // // Returns: // - err: nil when initialization succeeds; an error for invalid realm context or KV-store failure InitializeDistributedToGovStakerHistory(_ int, rlm realm) error // GetDistributedToGovStakerHistory returns cumulative actual GovStaker transfers by token. // // Returns: // - tree: persistent BPTree keyed by token path with int64 transferred amounts GetDistributedToGovStakerHistory() *bptree.BPTree // GetDistributedToGovStakerHistoryItem reads one token's actual GovStaker transfer. // // Parameters: // - tokenPath: token contract path used as the history-tree key // // Returns: // - amount: stored transferred amount in token base units, or zero when tokenPath is absent // - exists: true when tokenPath has a stored history amount; false when absent GetDistributedToGovStakerHistoryItem(tokenPath string) (int64, bool) // SetDistributedToGovStakerHistoryItem stores one token's actual GovStaker transfer. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // - tokenPath: token contract path used as the history-tree key // - amount: cumulative amount actually transferred to GovStaker in token base units // // Returns: // - err: nil when the value is stored; an error for invalid realm context, missing tree, or KV-store failure SetDistributedToGovStakerHistoryItem(_ int, rlm realm, tokenPath string, amount int64) error // HasDistributedToDevOpsHistoryStoreKey reports whether the DevOps history tree key exists. // // Returns: // - exists: true when the cumulative DevOps distribution-history tree is present HasDistributedToDevOpsHistoryStoreKey() bool // InitializeDistributedToDevOpsHistory creates the DevOps distribution-history tree. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // // Returns: // - err: nil when initialization succeeds; an error for invalid realm context or KV-store failure InitializeDistributedToDevOpsHistory(_ int, rlm realm) error // GetDistributedToDevOpsHistory returns cumulative actual DevOps transfers by token. // // Returns: // - tree: persistent BPTree keyed by token path with int64 transferred amounts GetDistributedToDevOpsHistory() *bptree.BPTree // GetDistributedToDevOpsHistoryItem reads one token's actual DevOps transfer. // // Parameters: // - tokenPath: token contract path used as the history-tree key // // Returns: // - amount: stored transferred amount in token base units, or zero when tokenPath is absent // - exists: true when tokenPath has a stored history amount; false when absent GetDistributedToDevOpsHistoryItem(tokenPath string) (int64, bool) // SetDistributedToDevOpsHistoryItem stores one token's actual DevOps transfer. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // - tokenPath: token contract path used as the history-tree key // - amount: cumulative amount actually transferred to DevOps in token base units // // Returns: // - err: nil when the value is stored; an error for invalid realm context, missing tree, or KV-store failure SetDistributedToDevOpsHistoryItem(_ int, rlm realm, tokenPath string, amount int64) error // HasReservedTokensStoreKey reports whether the reserved-token index key exists. // // Returns: // - exists: true when the reserved-token index is present in the KV store HasReservedTokensStoreKey() bool // InitializeReservedTokens creates the empty reserved-token index. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // // Returns: // - err: nil when initialization succeeds; an error for invalid realm context or KV-store failure InitializeReservedTokens(_ int, rlm realm) error // GetReservedTokens returns token paths currently marked as awaiting distribution. // // Returns: // - tokenPaths: keys in the reserved-token index GetReservedTokens() []string // HasReservedToken reports whether tokenPath is in the reserved-token index. // // Parameters: // - tokenPath: token contract path to look up in the reserved-token index // // Returns: // - exists: true when tokenPath is reserved for distribution HasReservedToken(tokenPath string) bool // AddReservedToken marks tokenPath as awaiting fee distribution. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // - tokenPath: token contract path to add to the reserved-token index // // Returns: // - err: nil when added or already present; an error for invalid realm context or KV-store failure AddReservedToken(_ int, rlm realm, tokenPath string) error // RemoveReservedToken removes tokenPath from the reserved-token index when present. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // - tokenPath: token contract path to remove from the reserved-token index // // Returns: // - err: nil when removed or already absent; an error for invalid realm context or KV-store failure RemoveReservedToken(_ int, rlm realm, tokenPath string) error // HasAccrualEpochStoreKey reports whether the accrual-epoch key exists. // // Returns: // - exists: true when the current accrual epoch is present in the KV store HasAccrualEpochStoreKey() bool // InitializeAccrualEpoch creates the accrual-epoch key with epoch zero. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // // Returns: // - err: nil when initialization succeeds; an error for invalid realm context or KV-store failure InitializeAccrualEpoch(_ int, rlm realm) error // GetAccrualEpoch reads the epoch currently assigned to newly collected fees. // // Returns: // - epoch: current non-negative accrual epoch GetAccrualEpoch() int64 // SetAccrualEpoch persists a new current accrual epoch. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // - accrualEpoch: epoch number to store for subsequently collected fees // // Returns: // - err: nil when the epoch is stored; an error for invalid realm context or KV-store failure SetAccrualEpoch(_ int, rlm realm, accrualEpoch int64) error // HasAccrualBucketsStoreKey reports whether the accrual-buckets tree key exists. // // Returns: // - exists: true when the per-token accrual-bucket tree is present HasAccrualBucketsStoreKey() bool // InitializeAccrualBuckets creates the empty per-token accrual-bucket tree. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // // Returns: // - err: nil when initialization succeeds; an error for invalid realm context or KV-store failure InitializeAccrualBuckets(_ int, rlm realm) error // GetAccrualBuckets reads the oldest pending buckets for one token without removing them. // // Parameters: // - tokenPath: token contract path whose accrual buckets are queried // - limit: maximum number of oldest buckets; zero or negative returns all pending buckets // // Returns: // - epochs: selected accrual epoch numbers in ascending order // - amounts: bucket amounts corresponding positionally to epochs, in token base units GetAccrualBuckets(tokenPath string, limit int) ([]int64, []int64) // AddAccrualBucket adds amount to one token's bucket for an accrual epoch. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // - tokenPath: token contract path whose accrual bucket is updated // - epoch: accrual epoch receiving the fee amount // - amount: fee amount in token base units to add to that epoch bucket // // Returns: // - err: nil when the bucket is stored; an error for invalid realm context or KV-store failure AddAccrualBucket(_ int, rlm realm, tokenPath string, epoch int64, amount int64) error // RemoveAccrualBuckets removes specified epoch buckets for one token. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // - tokenPath: token contract path whose buckets are removed // - epochs: accrual epoch numbers to remove; an empty result removes no buckets // // Returns: // - err: nil when removal succeeds; an error for invalid realm context or KV-store failure RemoveAccrualBuckets(_ int, rlm realm, tokenPath string, epochs []int64) error // HasAccrualPendingTokensStoreKey reports whether the pending-token index key exists. // // Returns: // - exists: true when the index of tokens with pending buckets is present HasAccrualPendingTokensStoreKey() bool // InitializeAccrualPendingTokens creates the empty pending-token index. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // // Returns: // - err: nil when initialization succeeds; an error for invalid realm context or KV-store failure InitializeAccrualPendingTokens(_ int, rlm realm) error // GetAccrualPendingTokens returns token paths indexed as owning pending buckets. // // Returns: // - tokenPaths: token contract paths present in the pending-token index GetAccrualPendingTokens() []string // AddAccrualPendingToken marks a token path as owning at least one pending bucket. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // - tokenPath: token contract path to add to the pending-token index // // Returns: // - err: nil when added or already present; an error for invalid realm context or KV-store failure AddAccrualPendingToken(_ int, rlm realm, tokenPath string) error // RemoveAccrualPendingToken removes a token path from the pending-token index when present. // // Parameters: // - _: cross-call discriminator; callers pass 0 // - rlm: propagated protocol_fee realm context required for the authorized store write // - tokenPath: token contract path to remove after its pending buckets are consumed // // Returns: // - err: nil when removed or already absent; an error for invalid realm context or KV-store failure RemoveAccrualPendingToken(_ int, rlm realm, tokenPath string) error }
- #22upgrade.gno
- #23package protocol_fee import ( "gno.land/r/gnoswap/access/v1" ) // RegisterInitializer registers a new protocol fee implementation version. // This function is called by each version (v1, v2, etc.) during initialization // to register their implementation with the proxy system. // // The initializer function creates a new instance of the implementation // using the provided protocolFeeStore interface. It receives a realm value // that resolves to the protocol_fee proxy realm — the only address with // write permission on the shared KV store — so any per-version store // bootstrapping performed inside the initializer passes the proxy's // authorization check. // // Security: Only contracts within the domain path can register initializers. // Each package path can only register once to prevent duplicate registrations. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // - initializer: callback that receives the proxy realm context and protocol-fee store, initializes version-specific state, and returns the implementation instance func RegisterInitializer(cur realm, initializer func(_ int, rlm realm, protocolFeeStore IProtocolFeeStore) IProtocolFee) { // `cur` captured here is the protocol_fee crossing frame. The wrapping // closure forwards it to the v1 initializer so any store writes the // initializer performs run under the proxy's identity rather than the // version package's, which would fail the kvStore ACL. initializerFunc := func(_ int, rlm realm, domainStore any) any { access.AssertIsRlmCurrent(0, rlm) currentProtocolFeeStore, ok := domainStore.(IProtocolFeeStore) if !ok { panic("domainStore is not an IProtocolFeeStore") } return initializer(0, rlm, currentProtocolFeeStore) } err := versionManager.RegisterInitializer(0, cur, initializerFunc) if err != nil { panic(err) } err = updateImplementation() if err != nil { panic(err) } } // UpgradeImpl switches the active protocol fee implementation to a different version. // This function allows seamless upgrades from one version to another without // data migration or downtime. // // Security: Only admin or governance can perform upgrades. // The new implementation must have been previously registered via RegisterInitializer. // // Parameters: // - cur: Current realm context; callers use cross(cur) when crossing into this realm. // - packagePath: registered implementation package path to activate func UpgradeImpl(cur realm, packagePath string) { // Ensure only admin or governance can perform upgrades prev := cur.Previous() access.AssertIsAdminOrGovernance(prev.Address()) err := versionManager.ChangeImplementation(0, cur, packagePath) if err != nil { panic(err) } err = updateImplementation() if err != nil { panic(err) } } // GetImplementationPackagePath returns the package path of the currently active implementation. // // Returns: // - packagePath: package path of the active implementation func GetImplementationPackagePath() string { return versionManager.GetCurrentPackagePath() }
- #24/gno.MemPackageType
Result log
msg:0,success:true,log:,events:[]