doc.gno
5.51 Kb · 144 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// 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) string:
36// Attempts to register a new referral and returns the effective referrer.
37// Empty input returns the user's stored referral without attempting registration.
38//
39// ## Workflow
40//
41// Typical usage of this contract follows these steps:
42//
43// 1. An authorized contract (router, staker, etc.) calls TryRegister
44// to resolve the effective referrer and optionally create a referral relationship.
45// 2. The keeper validates the caller's permissions via isValidCaller.
46// 3. Address validation ensures both addresses are valid and not
47// self-referencing.
48// 4. Rate limiting checks prevent operations more than once per 24 hours.
49// 5. The referral is stored in the tree and an event is emitted when registration occurs.
50//
51// ## Authorized Callers
52//
53// Only contracts with the following roles can modify referral data:
54//
55// - ROLE_GOVERNANCE: Governance contracts
56// - ROLE_GOV_STAKER: Governance staker contracts
57// - ROLE_ROUTER: Router contracts
58// - ROLE_POSITION: Position manager contracts
59// - ROLE_STAKER: Staker contracts
60// - ROLE_LAUNCHPAD: Launchpad contracts
61//
62// ## Rate Limiting
63//
64// To prevent abuse, the system enforces a 24-hour cooldown period between
65// operations for each address. This means:
66//
67// - A new referral can only be registered once per 24 hours per address
68// - Updates and removals are also subject to the same rate limit
69// - Attempting operations within the cooldown period returns ErrTooManyRequests
70//
71// ## Events
72//
73// The package emits the following events:
74//
75// - RegisterReferral: Emitted when a new referral is created
76// - ReferralRegistrationFailed: Emitted when non-empty referral registration fails
77//
78// ## Error Handling
79//
80// The package defines several error types:
81// - `ErrInvalidAddress`: Returned when an address format is invalid
82// - `ErrSelfReferral`: Returned when attempting to set self as referrer
83// - `ErrUnauthorized`: Returned when the caller lacks permission
84// - `ErrTooManyRequests`: Returned when rate limit is exceeded (24-hour cooldown)
85// - `ErrNotFound`: Returned when attempting to get a non-existent referral
86// - `ErrInvalidTime`: Returned when the stored timestamp format is invalid
87//
88// ## Example: Integration with Router Contract
89//
90// The router contract can register referrals during swap operations:
91//
92// ```go
93//
94// import (
95// "gno.land/r/gnoswap/referral"
96// )
97//
98// func SwapWithReferral(cur realm, referralCode string, ...) {
99// // Get the caller address
100// caller := cur.Previous().Address()
101//
102// actualReferrer := referral.TryRegister(cross(cur), caller, referralCode)
103//
104// // Continue with swap logic...
105// }
106//
107// ```
108//
109// ## Example: Checking Referral for Rewards
110//
111// Other contracts can check referral relationships for reward distribution:
112//
113// ```go
114//
115// import (
116// "gno.land/r/gnoswap/referral"
117// )
118//
119// func DistributeRewards(user address, amount uint64) {
120// // Check if user has a referrer
121// if referral.HasReferral(user.String()) {
122// referrerAddr := referral.GetReferral(user.String())
123// // Calculate and distribute referral bonus
124// referrerBonus := amount * referralRate / 100
125// sendReward(address(referrerAddr), referrerBonus)
126// }
127// }
128//
129// ```
130//
131// ## Limitations and Constraints
132//
133// - A user can have only one referrer at a time
134// - Self-referral is not allowed
135// - Operations are rate-limited to once per 24 hours per address
136// - Only authorized contracts can register/update/remove referrals
137// - Zero address as referrer triggers removal of the referral
138//
139// ## Notes
140//
141// - The contract uses RBAC (Role-Based Access Control) for authorization
142// - Rate limiting state persists across transactions
143// - Events are emitted for all state changes for off-chain tracking
144package referral