doc.gno
5.65 Kb ยท 153 lines
1// Package referral implements a referral system on Gno. It allows
2// authorized contracts to register, update, or remove referral
3// relationships. A referral link is defined as a mapping from one
4// address (the "user") to another address (the "referrer").
5//
6// ## Overview
7//
8// The referral package is composed of the following components:
9//
10// 1. **errors.gno**: Defines error types for various failure scenarios
11// such as invalid address, unauthorized caller, self-referral,
12// rate limit exceeded, and referral not found.
13// 2. **utils.gno**: Contains the isValidCaller function that checks
14// if the caller has an authorized role (governance, router, position,
15// staker, or launchpad contracts).
16// 3. **type.gno**: Defines the ReferralKeeper interface and event type
17// constants for register, update, and remove operations.
18// 4. **keeper.gno**: Implements the ReferralKeeper interface using an
19// AVL tree for storage. Includes rate limiting (24-hour cooldown)
20// to prevent abuse of referral operations.
21// 5. **global_keeper.gno**: Exposes the public API functions that
22// external contracts can use to interact with the referral system.
23//
24// ## Public API
25//
26// The package exposes the following public functions:
27//
28// - GetReferral(addr string) string: Returns the referrer address for
29// the given user address. Returns empty string if not found.
30// - HasReferral(addr string) bool: Returns true if the user has a
31// registered referrer.
32// - IsEmpty() bool: Returns true if no referral relationships exist.
33// - GetLastOpTimestamp(addr string) (int64, error): Returns the last
34// referral operation timestamp for the given user address.
35// - TryRegister(cur realm, addr address, referral string) bool:
36// Attempts to register a new referral. Returns true on success.
37//
38// ## Workflow
39//
40// Typical usage of this contract follows these steps:
41//
42// 1. An authorized contract (router, staker, etc.) calls TryRegister
43// to create a referral relationship.
44// 2. The keeper validates the caller's permissions via isValidCaller.
45// 3. Address validation ensures both addresses are valid and not
46// self-referencing.
47// 4. Rate limiting checks prevent operations more than once per 24 hours.
48// 5. The referral is stored in the AVL tree and an event is emitted.
49//
50// ## Authorized Callers
51//
52// Only contracts with the following roles can modify referral data:
53//
54// - ROLE_GOVERNANCE: Governance contracts
55// - ROLE_GOV_STAKER: Governance staker contracts
56// - ROLE_ROUTER: Router contracts
57// - ROLE_POSITION: Position manager contracts
58// - ROLE_STAKER: Staker contracts
59// - ROLE_LAUNCHPAD: Launchpad contracts
60//
61// ## Rate Limiting
62//
63// To prevent abuse, the system enforces a 24-hour cooldown period between
64// operations for each address. This means:
65//
66// - A new referral can only be registered once per 24 hours per address
67// - Updates and removals are also subject to the same rate limit
68// - Attempting operations within the cooldown period returns ErrTooManyRequests
69//
70// ## Events
71//
72// The package emits the following events:
73//
74// - RegisterReferral: Emitted when a new referral is created
75// - ReferralRegistrationFailed: Emitted when TryRegister fails
76//
77// ## Error Handling
78//
79// The package defines several error types:
80// - `ErrInvalidAddress`: Returned when an address format is invalid
81// - `ErrSelfReferral`: Returned when attempting to set self as referrer
82// - `ErrUnauthorized`: Returned when the caller lacks permission
83// - `ErrTooManyRequests`: Returned when rate limit is exceeded (24-hour cooldown)
84// - `ErrNotFound`: Returned when attempting to get a non-existent referral
85// - `ErrInvalidTime`: Returned when the stored timestamp format is invalid
86//
87// ## Example: Integration with Router Contract
88//
89// The router contract can register referrals during swap operations:
90//
91// ```go
92//
93// import (
94// "chain/runtime"
95//
96// "gno.land/r/gnoswap/referral"
97// )
98//
99// func SwapWithReferral(cur realm, referralCode string, ...) {
100// // Get the caller address
101// caller := runtime.PreviousRealm().Address()
102//
103// // Register the referral if provided
104// if referralCode != "" {
105// success := referral.TryRegister(cross, caller, referralCode)
106// actualReferrer := referralCode
107// if !success {
108// // Get existing referral if registration failed
109// actualReferrer = referral.GetReferral(caller.String())
110// }
111// }
112//
113// // Continue with swap logic...
114// }
115//
116// ```
117//
118// ## Example: Checking Referral for Rewards
119//
120// Other contracts can check referral relationships for reward distribution:
121//
122// ```go
123//
124// import (
125// "gno.land/r/gnoswap/referral"
126// )
127//
128// func DistributeRewards(user address, amount uint64) {
129// // Check if user has a referrer
130// if referral.HasReferral(user.String()) {
131// referrerAddr := referral.GetReferral(user.String())
132// // Calculate and distribute referral bonus
133// referrerBonus := amount * referralRate / 100
134// sendReward(address(referrerAddr), referrerBonus)
135// }
136// }
137//
138// ```
139//
140// ## Limitations and Constraints
141//
142// - A user can have only one referrer at a time
143// - Self-referral is not allowed
144// - Operations are rate-limited to once per 24 hours per address
145// - Only authorized contracts can register/update/remove referrals
146// - Zero address as referrer triggers removal of the referral
147//
148// ## Notes
149//
150// - The contract uses RBAC (Role-Based Access Control) for authorization
151// - Rate limiting state persists across transactions
152// - Events are emitted for all state changes for off-chain tracking
153package referral