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

pool.gno

45.72 Kb · 1396 lines
   1package staker
   2
   3import (
   4	"errors"
   5	"time"
   6
   7	"gno.land/p/gnoswap/consts/v1"
   8	i256 "gno.land/p/gnoswap/int256/v1"
   9	u256 "gno.land/p/gnoswap/uint256/v1"
  10	"gno.land/p/gnoswap/utils/v1"
  11	bptree "gno.land/p/nt/bptree/v0"
  12	ufmt "gno.land/p/nt/ufmt/v0"
  13)
  14
  15const AllTierCount = 4 // 0, 1, 2, 3
  16
  17// Pool is a struct for storing an incentivized pool information
  18// Each pool stores Incentives and Ticks associated with it.
  19//
  20// Fields:
  21// - poolPath: The path of the pool.
  22//
  23//   - stakedLiquidity:
  24//     The current total staked liquidity of the in-range positions for the pool.
  25//     Updated when tick cross happens or stake/unstake happens.
  26//     Used to calculate the global reward ratio accumulation or
  27//     decide whether to enter/exit unclaimable period.
  28//
  29//   - lastUnclaimableTime:
  30//     The time at which the unclaimable period started.
  31//     Set to 0 when the pool is not in an unclaimable period.
  32//
  33//   - unclaimableAcc:
  34//     The accumulated undistributed unclaimable reward.
  35//     Reset to 0 when processUnclaimableReward is called and sent to community pool.
  36//
  37//   - rewardCache:
  38//     The cached per-second reward emitted for this pool.
  39//     Stores new entry only when the reward is changed.
  40//     PoolTier.cacheReward() updates this.
  41//
  42// - incentives: The external incentives associated with the pool.
  43//
  44// - ticks: The Ticks associated with the pool.
  45//
  46//   - globalRewardRatioAccumulation:
  47//     Global ratio of Time / TotalStake accumulation(since the pool creation)
  48//     Stores new entry only when tick cross or stake/unstake happens.
  49//     It is used to calculate the reward for a staked position at certain time.
  50//
  51//   - historicalTick:
  52//     The historical tick for the pool at a given time.
  53//     It does not reflect the exact tick at the timestamp,
  54//     but it provides correct ordering for the staked position's ticks.
  55//     Therefore, you should not compare it for equality, only for ordering.
  56//     Set when tick cross happens or a new position is created.
  57type Pool struct {
  58	poolPath string
  59
  60	stakedLiquidity *UintTree // uint64 timestamp -> *u256.Uint(Q128)
  61
  62	lastUnclaimableTime int64
  63	unclaimableAcc      int64
  64
  65	rewardCache *UintTree // uint64 timestamp -> int64 gnsReward
  66
  67	incentives *Incentives
  68
  69	ticks Ticks // int32 tickId -> Tick tick
  70
  71	globalRewardRatioAccumulation *UintTree // uint64 timestamp -> *u256.Uint(Q128) rewardRatioAccumulation
  72
  73	historicalTick *UintTree // uint64 timestamp -> int32 tickId
  74}
  75
  76// Pool Getter/Setter methods
  77
  78// PoolPath returns the pool path
  79//
  80// Returns:
  81//   - path: Pool identifier used to associate this state with a liquidity pool.
  82func (p *Pool) PoolPath() string {
  83	return p.poolPath
  84}
  85
  86// SetPoolPath sets the pool path
  87//
  88// Parameters:
  89//   - poolPath: Pool identifier to store.
  90func (p *Pool) SetPoolPath(poolPath string) {
  91	p.poolPath = poolPath
  92}
  93
  94// StakedLiquidity returns the staked liquidity tree
  95//
  96// Returns:
  97//   - tree: Historical staked-liquidity tree keyed by Unix timestamp, with Q128-scaled liquidity values.
  98func (p *Pool) StakedLiquidity() *UintTree {
  99	return p.stakedLiquidity
 100}
 101
 102// SetStakedLiquidity sets the staked liquidity tree
 103//
 104// Parameters:
 105//   - stakedLiquidity: Tree of historical total staked liquidity snapshots.
 106func (p *Pool) SetStakedLiquidity(stakedLiquidity *UintTree) {
 107	p.stakedLiquidity = stakedLiquidity
 108}
 109
 110// SetStakedLiquidityAt records the current total staked liquidity at a timestamp.
 111//
 112// Parameters:
 113//   - currentTime: Nonnegative Unix timestamp in seconds used as the snapshot key.
 114//   - delta: Total staked liquidity at currentTime, stored as a copied Q128-scaled uint value.
 115func (p *Pool) SetStakedLiquidityAt(currentTime int64, delta *u256.Uint) {
 116	p.StakedLiquidity().Set(currentTime, u256.Zero().Set(delta))
 117}
 118
 119// LastUnclaimableTime returns the last unclaimable time
 120//
 121// Returns:
 122//   - timestamp: Unix timestamp in seconds at which the pool entered its current unclaimable period, or 0 when tracking has not started.
 123func (p *Pool) LastUnclaimableTime() int64 {
 124	return p.lastUnclaimableTime
 125}
 126
 127// SetLastUnclaimableTime sets the last unclaimable time
 128//
 129// Parameters:
 130//   - lastUnclaimableTime: Unix timestamp in seconds marking the start of the current unclaimable period; use 0 when no period is active.
 131func (p *Pool) SetLastUnclaimableTime(lastUnclaimableTime int64) {
 132	p.lastUnclaimableTime = lastUnclaimableTime
 133}
 134
 135// UnclaimableAcc returns the unclaimable accumulation
 136//
 137// Returns:
 138//   - amount: Accumulated undistributed unclaimable reward amount in the pool's int64 reward units.
 139func (p *Pool) UnclaimableAcc() int64 {
 140	return p.unclaimableAcc
 141}
 142
 143// SetUnclaimableAcc sets the unclaimable accumulation
 144//
 145// Parameters:
 146//   - unclaimableAcc: Accumulated undistributed unclaimable reward amount in the pool's int64 reward units.
 147func (p *Pool) SetUnclaimableAcc(unclaimableAcc int64) {
 148	p.unclaimableAcc = unclaimableAcc
 149}
 150
 151// RewardCache returns the reward cache tree
 152//
 153// Returns:
 154//   - tree: Historical per-second GNS reward cache keyed by Unix timestamp.
 155func (p *Pool) RewardCache() *UintTree {
 156	return p.rewardCache
 157}
 158
 159// SetRewardCache sets the reward cache tree
 160//
 161// Parameters:
 162//   - rewardCache: Tree containing the pool's per-second GNS reward snapshots.
 163func (p *Pool) SetRewardCache(rewardCache *UintTree) {
 164	p.rewardCache = rewardCache
 165}
 166
 167// SetRewardCacheAt records the per-second reward rate for a timestamp.
 168//
 169// Parameters:
 170//   - currentTime: Nonnegative Unix timestamp in seconds used as the cache key.
 171//   - reward: GNS reward emitted per second from currentTime until the next cached change.
 172func (p *Pool) SetRewardCacheAt(currentTime int64, reward int64) {
 173	p.RewardCache().Set(currentTime, reward)
 174}
 175
 176// Incentives returns the incentives
 177//
 178// Returns:
 179//   - incentives: External-incentive collection associated with this pool.
 180func (p *Pool) Incentives() *Incentives {
 181	return p.incentives
 182}
 183
 184// SetIncentives sets the incentives
 185//
 186// Parameters:
 187//   - incentives: External-incentive collection to associate with the pool.
 188func (p *Pool) SetIncentives(incentives *Incentives) {
 189	p.incentives = incentives
 190}
 191
 192// Ticks returns the ticks
 193//
 194// Returns:
 195//   - ticks: Addressable tick mapping for this pool's staked positions.
 196func (p *Pool) Ticks() *Ticks {
 197	return &p.ticks
 198}
 199
 200// SetTicks sets the ticks
 201//
 202// Parameters:
 203//   - ticks: Tick mapping value to store in the pool.
 204func (p *Pool) SetTicks(ticks Ticks) {
 205	p.ticks = ticks
 206}
 207
 208// GlobalRewardRatioAccumulation returns the global reward ratio accumulation tree
 209//
 210// Returns:
 211//   - tree: Historical global time-per-total-stake accumulation keyed by Unix timestamp.
 212func (p *Pool) GlobalRewardRatioAccumulation() *UintTree {
 213	return p.globalRewardRatioAccumulation
 214}
 215
 216// SetGlobalRewardRatioAccumulation sets the global reward ratio accumulation tree
 217//
 218// Parameters:
 219//   - globalRewardRatioAccumulation: Tree of serialized Q128-scaled reward-ratio accumulation snapshots.
 220func (p *Pool) SetGlobalRewardRatioAccumulation(globalRewardRatioAccumulation *UintTree) {
 221	p.globalRewardRatioAccumulation = globalRewardRatioAccumulation
 222}
 223
 224// SetGlobalRewardRatioAccumulationAt records a serialized global reward-ratio accumulation snapshot.
 225//
 226// Parameters:
 227//   - currentTime: Nonnegative Unix timestamp in seconds used as the snapshot key.
 228//   - acc: Serialized Q128-scaled global reward-ratio accumulation value.
 229func (p *Pool) SetGlobalRewardRatioAccumulationAt(currentTime int64, acc string) {
 230	p.GlobalRewardRatioAccumulation().Set(currentTime, acc)
 231}
 232
 233// HistoricalTick returns the historical tick tree
 234//
 235// Returns:
 236//   - tree: Historical tick-ID ordering snapshots keyed by Unix timestamp.
 237func (p *Pool) HistoricalTick() *UintTree {
 238	return p.historicalTick
 239}
 240
 241// SetHistoricalTick sets the historical tick tree
 242//
 243// Parameters:
 244//   - historicalTick: Tree of tick IDs representing the ordering history of staked positions.
 245func (p *Pool) SetHistoricalTick(historicalTick *UintTree) {
 246	p.historicalTick = historicalTick
 247}
 248
 249// SetHistoricalTickAt records the tick ordering snapshot for a timestamp.
 250//
 251// Parameters:
 252//   - currentTime: Nonnegative Unix timestamp in seconds used as the snapshot key.
 253//   - tick: Tick ID representing the pool's historical ordering at currentTime.
 254func (p *Pool) SetHistoricalTickAt(currentTime int64, tick int32) {
 255	p.HistoricalTick().Set(currentTime, tick)
 256}
 257
 258// Clone returns a pool copy of the scalar state with a fresh tick container.
 259//
 260// Returns:
 261//   - pool: Pool copy carrying scalar state and a fresh tick container; backing trees and incentives are nil, and nil receiver yields nil.
 262func (p *Pool) Clone() *Pool {
 263	if p == nil {
 264		return nil
 265	}
 266
 267	return &Pool{
 268		poolPath:                      p.poolPath,
 269		stakedLiquidity:               nil,
 270		lastUnclaimableTime:           p.lastUnclaimableTime,
 271		unclaimableAcc:                p.unclaimableAcc,
 272		rewardCache:                   nil,
 273		incentives:                    nil,
 274		ticks:                         NewTicks(),
 275		globalRewardRatioAccumulation: nil,
 276		historicalTick:                nil,
 277	}
 278}
 279
 280// NewPool creates pool reward state initialized at currentTime (Unix seconds).
 281//
 282// Parameters:
 283//   - poolPath: Pool identifier to store in the new reward-state object.
 284//   - currentTime: Unix timestamp in seconds used to seed the initial accumulation, reward-cache, and liquidity snapshots.
 285//
 286// Returns:
 287//   - pool: Initialized pool with empty backing structures and zero initial reward/liquidity snapshots at currentTime.
 288func NewPool(poolPath string, currentTime int64) *Pool {
 289	pool := &Pool{
 290		poolPath:        poolPath,
 291		stakedLiquidity: NewUintTreeN(64),
 292		// lastUnclaimableTime is initialized to 0, which means "tracking not started yet".
 293		// When the pool receives a tier assignment (or external incentive), `cacheReward` will be called,
 294		// which will automatically call `startUnclaimablePeriod` if the pool has zero liquidity.
 295		// This ensures proper unclaimable period tracking from the moment rewards start emitting.
 296		lastUnclaimableTime:           0,
 297		unclaimableAcc:                0,
 298		rewardCache:                   NewUintTreeN(64),
 299		incentives:                    NewIncentives(poolPath),
 300		ticks:                         NewTicks(),
 301		globalRewardRatioAccumulation: NewUintTreeN(64),
 302		historicalTick:                NewUintTreeN(64),
 303	}
 304
 305	pool.SetGlobalRewardRatioAccumulationAt(currentTime, "0")
 306
 307	// Initialize rewardCache to 0 to ensure `cacheReward` will trigger on first tier assignment
 308	pool.SetRewardCacheAt(currentTime, int64(0))
 309	pool.SetStakedLiquidityAt(currentTime, u256.Zero())
 310
 311	return pool
 312}
 313
 314// Incentives represents a collection of external incentives for a specific pool.
 315//
 316// Fields:
 317//
 318//   - incentives: BPTree storing ExternalIncentive objects indexed by incentiveId
 319//     The incentiveId serves as the key to efficiently lookup incentive details
 320//
 321//   - targetPoolPath: String identifier for the pool this incentive collection belongs to
 322//     Used to associate incentives with their corresponding liquidity pool
 323//
 324//   - unclaimablePeriods: Tree storing periods when rewards cannot be claimed
 325//     Maps start timestamp (key) to end timestamp (value)
 326//     An end timestamp of 0 indicates an ongoing unclaimable period
 327//     Used to track intervals when staking rewards are not claimable
 328//
 329//   - byStartTime: Per-pool start-time index mapping an incentive's start
 330//     timestamp (key) to the incentive IDs that start at that timestamp
 331//     (value). This mirrors the lazy-discovery lookup previously served by a
 332//     global creation-time index, but scoped to this pool's own incentives, so
 333//     discovery cost is bounded by this pool's incentives instead of growing
 334//     with the total number of incentives system-wide.
 335type Incentives struct {
 336	incentives *bptree.BPTree // (incentiveId) => ExternalIncentive
 337
 338	targetPoolPath string // The target pool path for this incentive collection
 339
 340	unclaimablePeriods *UintTree // blockTimestamp -> any
 341
 342	byStartTime *UintTree // startTimestamp -> []incentiveId
 343}
 344
 345// Incentives Getter/Setter methods
 346
 347// Incentives returns the incentives tree
 348//
 349// Returns:
 350//   - tree: Mutable B+ tree mapping incentive IDs to ExternalIncentive records.
 351func (i *Incentives) IncentiveTrees() *bptree.BPTree {
 352	return i.incentives
 353}
 354
 355// SetIncentives sets the incentives tree
 356//
 357// Parameters:
 358//   - incentives: B+ tree to use for incentive ID lookups and storage.
 359func (i *Incentives) SetIncentives(incentives *bptree.BPTree) {
 360	i.incentives = incentives
 361}
 362
 363// TargetPoolPath returns the target pool path
 364//
 365// Returns:
 366//   - path: Pool path to which this incentive collection belongs.
 367func (i *Incentives) TargetPoolPath() string {
 368	return i.targetPoolPath
 369}
 370
 371// SetTargetPoolPath sets the target pool path
 372//
 373// Parameters:
 374//   - targetPoolPath: Pool path to associate with this incentive collection.
 375func (i *Incentives) SetTargetPoolPath(targetPoolPath string) {
 376	i.targetPoolPath = targetPoolPath
 377}
 378
 379// UnclaimablePeriods returns the unclaimable periods tree
 380//
 381// Returns:
 382//   - periods: Tree mapping unclaimable-period start timestamps to end timestamps; end 0 denotes an open period.
 383func (i *Incentives) UnclaimablePeriods() *UintTree {
 384	return i.unclaimablePeriods
 385}
 386
 387// SetUnclaimablePeriods sets the unclaimable periods tree
 388//
 389// Parameters:
 390//   - unclaimablePeriods: Tree of reward-unclaimable intervals keyed by their start timestamps.
 391func (i *Incentives) SetUnclaimablePeriods(unclaimablePeriods *UintTree) {
 392	i.unclaimablePeriods = unclaimablePeriods
 393}
 394
 395// Incentive returns an incentive by ID
 396//
 397// Parameters:
 398//   - incentiveId: Identifier used to look up the external incentive record.
 399//
 400// Returns:
 401//   - incentive: Matching ExternalIncentive pointer, or nil when no value is stored for incentiveId.
 402//   - found: True only when the stored value has ExternalIncentive type; false for a missing or mismatched value.
 403func (i *Incentives) Incentive(incentiveId string) (*ExternalIncentive, bool) {
 404	value := i.incentives.Get(incentiveId)
 405	if value == nil {
 406		return nil, false
 407	}
 408	incentive, ok := value.(*ExternalIncentive)
 409	return incentive, ok
 410}
 411
 412// SetIncentive sets an incentive by ID
 413//
 414// Parameters:
 415//   - incentiveId: Identifier under which to store the incentive.
 416//   - incentive: ExternalIncentive record to store for incentiveId.
 417func (i *Incentives) SetIncentive(incentiveId string, incentive *ExternalIncentive) {
 418	i.incentives.Set(incentiveId, incentive)
 419}
 420
 421// RemoveIncentive deletes an incentive by ID.
 422//
 423// Only an incentive that never started may be removed: once rewards begin
 424// accruing, deposits and the refund accounting reference the record, so an
 425// ended incentive is marked refunded instead of removed.
 426//
 427// Parameters:
 428//   - incentiveId: Identifier of the external incentive record to remove.
 429func (i *Incentives) RemoveIncentive(incentiveId string) {
 430	i.incentives.Remove(incentiveId)
 431}
 432
 433// SetUnclaimablePeriod records an interval during which staking rewards cannot be claimed.
 434//
 435// Parameters:
 436//   - startTimestamp: Nonnegative Unix timestamp in seconds at which the interval begins.
 437//   - endTimestamp: Unix timestamp in seconds at which the interval ends; 0 records an ongoing interval.
 438func (i *Incentives) SetUnclaimablePeriod(startTimestamp int64, endTimestamp int64) {
 439	i.unclaimablePeriods.Set(startTimestamp, endTimestamp)
 440}
 441
 442// RemoveUnclaimablePeriod removes the interval keyed by its start timestamp.
 443//
 444// Parameters:
 445//   - startTimestamp: Nonnegative Unix timestamp in seconds identifying the interval to remove.
 446func (i *Incentives) RemoveUnclaimablePeriod(startTimestamp int64) {
 447	i.unclaimablePeriods.Remove(startTimestamp)
 448}
 449
 450// IterateIncentives iterates over all incentives
 451//
 452// Parameters:
 453//   - fn: Callback receiving each incentive ID and record; returning true requests that iteration stop.
 454func (i *Incentives) IterateIncentives(fn func(incentiveId string, incentive *ExternalIncentive) bool) {
 455	i.incentives.Iterate("", "", func(key string, value interface{}) bool {
 456		if incentive, ok := value.(*ExternalIncentive); ok {
 457			return fn(key, incentive)
 458		}
 459		return false
 460	})
 461}
 462
 463// AddIncentiveByStartTime registers an incentive ID under its start timestamp
 464// in the per-pool start-time index. Multiple incentives starting at the same
 465// timestamp are accumulated as a list.
 466//
 467// Parameters:
 468//   - startTimestamp: Nonnegative Unix timestamp in seconds used as the start-time index key.
 469//   - incentiveId: Incentive identifier to append to that timestamp's bucket.
 470func (i *Incentives) AddIncentiveByStartTime(startTimestamp int64, incentiveId string) {
 471	var incentiveIds []string
 472	if value, ok := i.byStartTime.Get(startTimestamp); ok {
 473		if ids, ok := value.([]string); ok {
 474			incentiveIds = ids
 475		}
 476	}
 477	incentiveIds = append(incentiveIds, incentiveId)
 478	i.byStartTime.Set(startTimestamp, incentiveIds)
 479}
 480
 481// RemoveIncentiveByStartTime unregisters an incentive ID from the per-pool
 482// start-time index. The bucket itself is dropped once its last ID is removed,
 483// so the index never keeps an empty entry that discovery would still visit.
 484//
 485// Parameters:
 486//   - startTimestamp: Nonnegative Unix timestamp in seconds identifying the start-time bucket.
 487//   - incentiveId: Incentive identifier to remove from that bucket; all matching entries are omitted.
 488func (i *Incentives) RemoveIncentiveByStartTime(startTimestamp int64, incentiveId string) {
 489	value, ok := i.byStartTime.Get(startTimestamp)
 490	if !ok {
 491		return
 492	}
 493
 494	ids, ok := value.([]string)
 495	if !ok {
 496		return
 497	}
 498
 499	remaining := make([]string, 0, len(ids))
 500	for _, id := range ids {
 501		if id == incentiveId {
 502			continue
 503		}
 504		remaining = append(remaining, id)
 505	}
 506
 507	if len(remaining) == 0 {
 508		i.byStartTime.Remove(startTimestamp)
 509		return
 510	}
 511
 512	i.byStartTime.Set(startTimestamp, remaining)
 513}
 514
 515// IterateIncentiveIdsByTime iterates over the incentive IDs that start within
 516// the inclusive [startTime, endTime] range, visiting only the buckets that
 517// fall in the range. ReverseIterate is used because it is inclusive on both
 518// ends, matching the discovery semantics previously implemented as
 519// (startTimestamp >= startTime && startTimestamp <= endTime).
 520//
 521// Parameters:
 522//   - startTime: Inclusive lower Unix timestamp bound for incentive starts.
 523//   - endTime: Inclusive upper Unix timestamp bound for incentive starts.
 524//   - fn: Callback receiving each matching incentive ID; returning true requests that iteration stop.
 525func (i *Incentives) IterateIncentiveIdsByTime(startTime, endTime int64, fn func(incentiveId string) bool) {
 526	i.byStartTime.ReverseIterate(startTime, endTime, func(_ int64, value any) bool {
 527		incentiveIds, ok := value.([]string)
 528		if !ok {
 529			return false
 530		}
 531		for _, incentiveId := range incentiveIds {
 532			if fn(incentiveId) {
 533				return true
 534			}
 535		}
 536		return false
 537	})
 538}
 539
 540// NewIncentives creates an incentive collection for a pool and starts an open unclaimable period at the current time.
 541//
 542// Parameters:
 543//   - targetPoolPath: Pool path to associate with the new incentive collection.
 544//
 545// Returns:
 546//   - incentives: Collection with initialized incentive, start-time, and unclaimable-period trees.
 547func NewIncentives(targetPoolPath string) *Incentives {
 548	result := &Incentives{
 549		targetPoolPath:     targetPoolPath,
 550		unclaimablePeriods: NewUintTreeN(64),
 551		incentives:         bptree.NewBPTreeN(16),
 552		byStartTime:        NewUintTreeN(64),
 553	}
 554
 555	// initial unclaimable period starts, as there cannot be any staked positions yet.
 556	currentTimestamp := time.Now().Unix()
 557	result.SetUnclaimablePeriod(currentTimestamp, int64(0))
 558	return result
 559}
 560
 561type ExternalIncentive struct {
 562	incentiveId              string     // incentive id
 563	startTimestamp           int64      // start time for external reward
 564	endTimestamp             int64      // end time for external reward
 565	createdHeight            int64      // block height when the incentive was created
 566	createdTimestamp         int64      // timestamp when the incentive was created
 567	depositGnsAmount         int64      // deposited gns amount
 568	targetPoolPath           string     // external reward target pool path
 569	rewardToken              string     // external reward token path
 570	totalRewardAmount        int64      // total reward amount
 571	rewardAmount             int64      // mutable remaining reward amount
 572	rewardPerSecondX128      *u256.Uint // reward per second, scaled by 2^128 to preserve sub-second precision
 573	distributedRewardAmount  int64      // reward amount delivered to positions or refunded at incentive end
 574	accumulatedPenaltyAmount int64      // accumulated warmup penalty from CollectReward
 575	creator                  address    // creator address
 576
 577	refunded bool // whether EndExternalIncentive finalized the incentive and returned its refundable portion and GNS deposit
 578
 579	unclaimableSeconds int64 // accumulated seconds of unclaimable periods overlapping the incentive window
 580}
 581
 582// ExternalIncentive Getter/Setter methods
 583
 584// IncentiveId returns the incentive ID
 585//
 586// Returns:
 587//   - id: Identifier assigned to this external incentive.
 588func (e *ExternalIncentive) IncentiveId() string {
 589	return e.incentiveId
 590}
 591
 592// SetIncentiveId sets the incentive ID
 593//
 594// Parameters:
 595//   - incentiveId: Identifier to store on the incentive record.
 596func (e *ExternalIncentive) SetIncentiveId(incentiveId string) {
 597	e.incentiveId = incentiveId
 598}
 599
 600// StartTimestamp returns the start timestamp.
 601//
 602// It keys the byStartTime discovery index and must stay immutable after the
 603// incentive is registered, so no setter is exposed.
 604//
 605// Returns:
 606//   - timestamp: Unix timestamp in seconds at which reward distribution starts.
 607func (e *ExternalIncentive) StartTimestamp() int64 {
 608	return e.startTimestamp
 609}
 610
 611// EndTimestamp returns the end timestamp
 612//
 613// Returns:
 614//   - timestamp: Unix timestamp in seconds at which the incentive window ends.
 615func (e *ExternalIncentive) EndTimestamp() int64 {
 616	return e.endTimestamp
 617}
 618
 619// SetEndTimestamp sets the end timestamp
 620//
 621// Parameters:
 622//   - endTimestamp: Unix timestamp in seconds at which the incentive window ends.
 623func (e *ExternalIncentive) SetEndTimestamp(endTimestamp int64) {
 624	e.endTimestamp = endTimestamp
 625}
 626
 627// CreatedHeight returns the created height
 628//
 629// Returns:
 630//   - height: Block height at which the incentive record was created.
 631func (e *ExternalIncentive) CreatedHeight() int64 {
 632	return e.createdHeight
 633}
 634
 635// SetCreatedHeight sets the created height
 636//
 637// Parameters:
 638//   - createdHeight: Block height to record as the incentive's creation height.
 639func (e *ExternalIncentive) SetCreatedHeight(createdHeight int64) {
 640	e.createdHeight = createdHeight
 641}
 642
 643// CreatedTimestamp returns the created timestamp
 644//
 645// Returns:
 646//   - timestamp: Unix timestamp in seconds at which the incentive record was created.
 647func (e *ExternalIncentive) CreatedTimestamp() int64 {
 648	return e.createdTimestamp
 649}
 650
 651// SetCreatedTimestamp sets the created timestamp
 652//
 653// Parameters:
 654//   - createdTimestamp: Unix timestamp in seconds to record as the incentive creation time.
 655func (e *ExternalIncentive) SetCreatedTimestamp(createdTimestamp int64) {
 656	e.createdTimestamp = createdTimestamp
 657}
 658
 659// DepositGnsAmount returns the deposit GNS amount
 660//
 661// Returns:
 662//   - amount: GNS amount deposited to back this external incentive.
 663func (e *ExternalIncentive) DepositGnsAmount() int64 {
 664	return e.depositGnsAmount
 665}
 666
 667// SetDepositGnsAmount sets the deposit GNS amount
 668//
 669// Parameters:
 670//   - depositGnsAmount: GNS amount deposited to back this external incentive.
 671func (e *ExternalIncentive) SetDepositGnsAmount(depositGnsAmount int64) {
 672	e.depositGnsAmount = depositGnsAmount
 673}
 674
 675// TargetPoolPath returns the target pool path
 676//
 677// Returns:
 678//   - path: Pool path targeted by this external incentive.
 679func (e *ExternalIncentive) TargetPoolPath() string {
 680	return e.targetPoolPath
 681}
 682
 683// SetTargetPoolPath sets the target pool path
 684//
 685// Parameters:
 686//   - targetPoolPath: Pool path to target with this external incentive.
 687func (e *ExternalIncentive) SetTargetPoolPath(targetPoolPath string) {
 688	e.targetPoolPath = targetPoolPath
 689}
 690
 691// RewardToken returns the reward token
 692//
 693// Returns:
 694//   - token: Reward-token path distributed by this incentive.
 695func (e *ExternalIncentive) RewardToken() string {
 696	return e.rewardToken
 697}
 698
 699// SetRewardToken sets the reward token
 700//
 701// Parameters:
 702//   - rewardToken: Token path of the reward asset distributed by this incentive.
 703func (e *ExternalIncentive) SetRewardToken(rewardToken string) {
 704	e.rewardToken = rewardToken
 705}
 706
 707// TotalRewardAmount returns the total reward amount
 708//
 709// Returns:
 710//   - amount: Total reward amount configured when the incentive was created.
 711func (e *ExternalIncentive) TotalRewardAmount() int64 {
 712	return e.totalRewardAmount
 713}
 714
 715// SetTotalRewardAmount sets the total reward amount
 716//
 717// Parameters:
 718//   - totalRewardAmount: Total reward amount to record for the incentive.
 719func (e *ExternalIncentive) SetTotalRewardAmount(totalRewardAmount int64) {
 720	e.totalRewardAmount = totalRewardAmount
 721}
 722
 723// RewardAmount returns the reward amount
 724//
 725// Returns:
 726//   - amount: Mutable reward amount remaining after distributions and refunds.
 727func (e *ExternalIncentive) RewardAmount() int64 {
 728	return e.rewardAmount
 729}
 730
 731// SetRewardAmount sets the reward amount
 732//
 733// Parameters:
 734//   - rewardAmount: Remaining reward amount to store after accounting adjustments.
 735func (e *ExternalIncentive) SetRewardAmount(rewardAmount int64) {
 736	e.rewardAmount = rewardAmount
 737}
 738
 739// RewardPerSecondX128 returns the Q128-scaled reward per second.
 740// The underlying value is (rewardAmount << 128) / duration.
 741//
 742// Returns:
 743//   - rate: Reward emitted per second, scaled by 2^128 for fixed-point accounting.
 744func (e *ExternalIncentive) RewardPerSecondX128() *u256.Uint {
 745	return e.rewardPerSecondX128
 746}
 747
 748// SetRewardPerSecondX128 sets the Q128-scaled reward per second.
 749//
 750// Parameters:
 751//   - rewardPerSecondX128: Q128-scaled per-second reward rate; the value is copied before storage.
 752func (e *ExternalIncentive) SetRewardPerSecondX128(rewardPerSecondX128 *u256.Uint) {
 753	e.rewardPerSecondX128 = u256.Zero().Set(rewardPerSecondX128)
 754}
 755
 756// DistributedRewardAmount returns the distributed reward amount
 757//
 758// Returns:
 759//   - amount: Reward amount already delivered to positions or refunded at incentive end.
 760func (e *ExternalIncentive) DistributedRewardAmount() int64 {
 761	return e.distributedRewardAmount
 762}
 763
 764// SetDistributedRewardAmount sets the distributed reward amount
 765//
 766// Parameters:
 767//   - distributedRewardAmount: Reward amount delivered to positions or refunded at incentive end.
 768func (e *ExternalIncentive) SetDistributedRewardAmount(distributedRewardAmount int64) {
 769	e.distributedRewardAmount = distributedRewardAmount
 770}
 771
 772// AccumulatedPenaltyAmount returns the accumulated warmup penalty amount
 773//
 774// Returns:
 775//   - amount: Warm-up penalty accumulated from reward collections for this incentive.
 776func (e *ExternalIncentive) AccumulatedPenaltyAmount() int64 {
 777	return e.accumulatedPenaltyAmount
 778}
 779
 780// SetAccumulatedPenaltyAmount sets the accumulated warmup penalty amount
 781//
 782// Parameters:
 783//   - accumulatedPenaltyAmount: Warm-up penalty amount to store in the incentive's accumulated accounting.
 784func (e *ExternalIncentive) SetAccumulatedPenaltyAmount(accumulatedPenaltyAmount int64) {
 785	e.accumulatedPenaltyAmount = accumulatedPenaltyAmount
 786}
 787
 788// Creator returns the creator address
 789//
 790// Returns:
 791//   - creator: Address that created and funded the incentive.
 792func (e *ExternalIncentive) Creator() address {
 793	return e.creator
 794}
 795
 796// SetCreator sets the creator address
 797//
 798// Parameters:
 799//   - creator: Address to record as the incentive creator and refund recipient.
 800func (e *ExternalIncentive) SetCreator(creator address) {
 801	e.creator = creator
 802}
 803
 804// Refunded returns the refunded status
 805//
 806// Returns:
 807//   - refunded: True when incentive finalization has marked its refundable balances as returned.
 808func (e *ExternalIncentive) Refunded() bool {
 809	return e.refunded
 810}
 811
 812// SetRefunded sets the refunded status
 813//
 814// Parameters:
 815//   - refunded: Finalization status to store for the incentive.
 816func (e *ExternalIncentive) SetRefunded(refunded bool) {
 817	e.refunded = refunded
 818}
 819
 820// UnclaimableSeconds returns the accumulated seconds of unclaimable periods
 821// that overlap the incentive window. It is updated whenever an unclaimable
 822// period closes and is backfilled once from the historical unclaimable
 823// periods tree after an upgrade.
 824//
 825// Returns:
 826//   - seconds: Accumulated seconds of unclaimable periods overlapping the incentive window.
 827func (e *ExternalIncentive) UnclaimableSeconds() int64 {
 828	return e.unclaimableSeconds
 829}
 830
 831// SetUnclaimableSeconds sets the accumulated unclaimable seconds.
 832//
 833// Parameters:
 834//   - unclaimableSeconds: Overlapping unclaimable duration in seconds to store.
 835func (e *ExternalIncentive) SetUnclaimableSeconds(unclaimableSeconds int64) {
 836	e.unclaimableSeconds = unclaimableSeconds
 837}
 838
 839// Clone returns an independent ExternalIncentive value with scalar fields copied and its fixed-point rate duplicated.
 840//
 841// Returns:
 842//   - incentive: Copied external incentive record; its fixed-point rate is cloned when present and otherwise initialized to zero.
 843func (e *ExternalIncentive) Clone() *ExternalIncentive {
 844	rewardPerSecondX128 := u256.Zero()
 845
 846	if e.rewardPerSecondX128 != nil {
 847		rewardPerSecondX128 = e.rewardPerSecondX128.Clone()
 848	}
 849
 850	return &ExternalIncentive{
 851		incentiveId:              e.incentiveId,
 852		startTimestamp:           e.startTimestamp,
 853		endTimestamp:             e.endTimestamp,
 854		createdHeight:            e.createdHeight,
 855		createdTimestamp:         e.createdTimestamp,
 856		depositGnsAmount:         e.depositGnsAmount,
 857		targetPoolPath:           e.targetPoolPath,
 858		rewardToken:              e.rewardToken,
 859		totalRewardAmount:        e.totalRewardAmount,
 860		rewardAmount:             e.rewardAmount,
 861		rewardPerSecondX128:      rewardPerSecondX128,
 862		creator:                  e.creator,
 863		refunded:                 e.refunded,
 864		unclaimableSeconds:       e.unclaimableSeconds,
 865		distributedRewardAmount:  e.distributedRewardAmount,
 866		accumulatedPenaltyAmount: e.accumulatedPenaltyAmount,
 867	}
 868}
 869
 870// NewExternalIncentive creates a new external incentive
 871//
 872// Parameters:
 873//   - incentiveId: Unique identifier assigned to the external incentive.
 874//   - targetPoolPath: Pool path whose staked positions may receive this incentive.
 875//   - rewardToken: Token path of the asset deposited for distribution.
 876//   - rewardAmount: Total reward amount, also used as the initial remaining reward balance.
 877//   - startTimestamp: Unix timestamp in seconds when reward distribution starts.
 878//   - endTimestamp: Unix timestamp in seconds when reward distribution ends; duration is endTimestamp-startTimestamp and must be nonzero for the rate calculation.
 879//   - creator: Address that funds the incentive and receives refunds at finalization.
 880//   - depositGnsAmount: GNS amount deposited as the incentive's required collateral.
 881//   - createdHeight: Block height to record for incentive creation.
 882//   - currentTime: Unix timestamp in seconds recorded as the incentive creation time.
 883//
 884// Returns:
 885//   - incentive: New incentive with a Q128-scaled per-second rate and zeroed distribution, penalty, and unclaimable counters.
 886func NewExternalIncentive(
 887	incentiveId string,
 888	targetPoolPath string,
 889	rewardToken string,
 890	rewardAmount int64,
 891	startTimestamp int64, // timestamp is in unix time(seconds)
 892	endTimestamp int64,
 893	creator address,
 894	depositGnsAmount int64,
 895	createdHeight int64,
 896	currentTime int64, // current time in unix time(seconds)
 897) *ExternalIncentive {
 898	incentiveDuration := endTimestamp - startTimestamp
 899
 900	// Compute reward per second scaled by 2^128 to preserve sub-second precision.
 901	// rewardPerSecondX128 = (rewardAmount << 128) / incentiveDuration.
 902	// Consumers must divide by 2^128 when materializing back to a plain integer.
 903	rewardPerSecondX128 := u256.MulDiv(
 904		u256.NewUintFromInt64(rewardAmount),
 905		consts.Q128(),
 906		u256.NewUintFromInt64(incentiveDuration),
 907	)
 908
 909	return &ExternalIncentive{
 910		incentiveId:              incentiveId,
 911		targetPoolPath:           targetPoolPath,
 912		rewardToken:              rewardToken,
 913		totalRewardAmount:        rewardAmount,
 914		rewardAmount:             rewardAmount,
 915		startTimestamp:           startTimestamp,
 916		endTimestamp:             endTimestamp,
 917		rewardPerSecondX128:      rewardPerSecondX128,
 918		distributedRewardAmount:  0,
 919		accumulatedPenaltyAmount: 0,
 920		creator:                  creator,
 921		createdHeight:            createdHeight,
 922		createdTimestamp:         currentTime,
 923		depositGnsAmount:         depositGnsAmount,
 924		refunded:                 false,
 925		unclaimableSeconds:       0,
 926	}
 927}
 928
 929// Tick mapping for each pool
 930type Ticks struct {
 931	tree *bptree.BPTree // int32 tickId -> tick
 932}
 933
 934// Ticks Getter/Setter methods
 935
 936// Tree returns the ticks tree
 937//
 938// Returns:
 939//   - tree: Mutable B+ tree mapping encoded int32 tick IDs to Tick records.
 940func (t *Ticks) Tree() *bptree.BPTree {
 941	return t.tree
 942}
 943
 944// SetTree sets the ticks tree
 945//
 946// Parameters:
 947//   - tree: B+ tree to use for tick storage.
 948func (t *Ticks) SetTree(tree *bptree.BPTree) {
 949	t.tree = tree
 950}
 951
 952// Get returns the tick for the given tickId, or nil if it does not exist.
 953//
 954// Parameters:
 955//   - tickId: Tick index to look up.
 956//
 957// Returns:
 958//   - tick: Matching Tick record, or nil when the encoded ID is absent; panics if a stored value has the wrong type.
 959func (t *Ticks) Get(tickId int32) *Tick {
 960	v := t.tree.Get(utils.EncodeInt32(tickId))
 961	if v == nil {
 962		return nil
 963	}
 964
 965	tick, ok := v.(*Tick)
 966	if !ok {
 967		panic("failed to cast value to *Tick")
 968	}
 969	return tick
 970}
 971
 972// Has reports whether a tick ID is present in the underlying tree.
 973//
 974// Parameters:
 975//   - tickId: Tick index whose encoded key is tested.
 976//
 977// Returns:
 978//   - present: True when the encoded tick ID exists in the underlying tree.
 979func (self *Ticks) Has(tickId int32) bool {
 980	return self.tree.Has(utils.EncodeInt32(tickId))
 981}
 982
 983// SetTick sets a tick by ID
 984//
 985// Parameters:
 986//   - tickId: Tick index under which to store the record.
 987//   - tick: Non-nil Tick record to store; a zero gross staked liquidity removes the tick instead, while nil input panics during the gross-liquidity check.
 988func (t *Ticks) SetTick(tickId int32, tick *Tick) {
 989	if tick.stakedLiquidityGross.IsZero() {
 990		t.tree.Remove(utils.EncodeInt32(tickId))
 991		return
 992	}
 993
 994	t.tree.Set(utils.EncodeInt32(tickId), tick)
 995}
 996
 997// IterateTicks iterates over all ticks
 998//
 999// Parameters:
1000//   - fn: Callback receiving each stored Tick and decoded tick ID; returning true requests that iteration stop.
1001func (t *Ticks) IterateTicks(fn func(tickId int32, tick *Tick) bool) {
1002	t.tree.Iterate("", "", func(key string, value interface{}) bool {
1003		tick, ok := value.(*Tick)
1004		if !ok {
1005			return false
1006		}
1007
1008		return fn(utils.DecodeInt32(key), tick)
1009	})
1010}
1011
1012// Clone returns a deep copy of ticks.
1013//
1014// Returns:
1015//   - ticks: New Ticks value with cloned Tick records in a fresh fanout-16 tree.
1016func (t Ticks) Clone() Ticks {
1017	cloned := bptree.NewBPTreeN(16)
1018	t.tree.Iterate("", "", func(key string, value any) bool {
1019		tick, ok := value.(*Tick)
1020		if !ok {
1021			panic("failed to cast value to *Tick")
1022		}
1023		cloned.Set(key, tick.Clone())
1024		return false
1025	})
1026	return Ticks{tree: cloned}
1027}
1028
1029// NewTicks creates an empty tick mapping with a fanout-16 B+ tree.
1030//
1031// Returns:
1032//   - ticks: Empty Ticks value ready to store pool tick records.
1033func NewTicks() Ticks {
1034	return Ticks{
1035		tree: bptree.NewBPTreeN(16),
1036	}
1037}
1038
1039// Tick represents the state of a specific tick in a pool.
1040//
1041// Fields:
1042// - id (int32): The ID of the tick.
1043// - stakedLiquidityGross (*u256.Uint): Total gross staked liquidity at this tick.
1044// - stakedLiquidityDelta (*i256.Int): Net change in staked liquidity at this tick.
1045// - outsideAccumulation (*UintTree): RewardRatioAccumulation outside the tick.
1046type Tick struct {
1047	id int32
1048
1049	// conceptually equal with Pool.liquidityGross but only for the staked positions
1050	stakedLiquidityGross *u256.Uint
1051
1052	// conceptually equal with Pool.liquidityNet but only for the staked positions
1053	stakedLiquidityDelta *i256.Int
1054
1055	// currentOutsideAccumulation is the accumulation of the time / TotalStake outside the tick.
1056	// It is calculated by subtracting the current tick's currentOutsideAccumulation from the global reward ratio accumulation.
1057	outsideAccumulation *UintTree // timestamp -> fixed 32-byte big-endian string
1058}
1059
1060// Tick Getter/Setter methods
1061
1062// Id returns the tick ID
1063//
1064// Returns:
1065//   - id: Tick index represented by this record.
1066func (t *Tick) Id() int32 {
1067	return t.id
1068}
1069
1070// SetId sets the tick ID
1071//
1072// Parameters:
1073//   - id: Tick index to store in the record.
1074func (t *Tick) SetId(id int32) {
1075	t.id = id
1076}
1077
1078// StakedLiquidityGross returns the staked liquidity gross
1079//
1080// Returns:
1081//   - liquidity: Total gross staked liquidity currently associated with this tick.
1082func (t *Tick) StakedLiquidityGross() *u256.Uint {
1083	return t.stakedLiquidityGross
1084}
1085
1086// SetStakedLiquidityGross sets the staked liquidity gross
1087//
1088// Parameters:
1089//   - stakedLiquidityGross: New total gross staked liquidity; the value is copied before storage.
1090func (t *Tick) SetStakedLiquidityGross(stakedLiquidityGross *u256.Uint) {
1091	t.stakedLiquidityGross = u256.Zero().Set(stakedLiquidityGross)
1092}
1093
1094// StakedLiquidityDelta returns the staked liquidity delta
1095//
1096// Returns:
1097//   - delta: Net staked liquidity change associated with this tick.
1098func (t *Tick) StakedLiquidityDelta() *i256.Int {
1099	return t.stakedLiquidityDelta
1100}
1101
1102// SetStakedLiquidityDelta sets the staked liquidity delta
1103//
1104// Parameters:
1105//   - stakedLiquidityDelta: New net staked liquidity delta; the value is copied before storage.
1106func (t *Tick) SetStakedLiquidityDelta(stakedLiquidityDelta *i256.Int) {
1107	t.stakedLiquidityDelta = i256.Zero().Set(stakedLiquidityDelta)
1108}
1109
1110// OutsideAccumulation returns the outside accumulation tree
1111//
1112// Returns:
1113//   - tree: Historical reward-ratio accumulation observed outside this tick.
1114func (t *Tick) OutsideAccumulation() *UintTree {
1115	return t.outsideAccumulation
1116}
1117
1118// SetOutsideAccumulation sets the outside accumulation tree
1119//
1120// Parameters:
1121//   - outsideAccumulation: UintTree of outside-accumulation snapshots keyed by Unix timestamp.
1122func (t *Tick) SetOutsideAccumulation(outsideAccumulation *UintTree) {
1123	t.outsideAccumulation = outsideAccumulation
1124}
1125
1126// SetOutsideAccumulationAt sets the outside accumulation at the timestamp.
1127// SetOutsideAccumulationAt records the Q128-scaled outside accumulation at a timestamp.
1128//
1129// Parameters:
1130//   - timestamp: Nonnegative Unix timestamp in seconds used as the snapshot key.
1131//   - acc: Q128-scaled accumulation value encoded into the tree.
1132func (t *Tick) SetOutsideAccumulationAt(timestamp int64, acc *u256.Uint) {
1133	t.outsideAccumulation.Set(timestamp, utils.EncodeUint256(acc))
1134}
1135
1136// Clone returns a deep copy of the tick.
1137//
1138// Returns:
1139//   - tick: Deep copy of the tick and its outside-accumulation tree, or nil when the receiver is nil.
1140func (t *Tick) Clone() *Tick {
1141	if t == nil {
1142		return nil
1143	}
1144
1145	return &Tick{
1146		id:                   t.id,
1147		stakedLiquidityGross: t.stakedLiquidityGross.Clone(),
1148		stakedLiquidityDelta: t.stakedLiquidityDelta.Clone(),
1149		outsideAccumulation:  t.outsideAccumulation.Clone(),
1150	}
1151}
1152
1153// NewTick creates a tick with zero staked liquidity and an empty fanout-4 outside-accumulation tree.
1154//
1155// Parameters:
1156//   - tickId: Tick index to assign to the new record.
1157//
1158// Returns:
1159//   - tick: Initialized tick record.
1160func NewTick(tickId int32) *Tick {
1161	return &Tick{
1162		id:                   tickId,
1163		stakedLiquidityGross: u256.Zero(),
1164		stakedLiquidityDelta: i256.Zero(),
1165		outsideAccumulation:  NewUintTreeN(4),
1166	}
1167}
1168
1169// 100%, 0%, 0% if no tier2 and tier3
1170// 80%, 0%, 20% if no tier2
1171// 70%, 30%, 0% if no tier3
1172// 50%, 30%, 20% if has tier2 and tier3
1173type TierRatio struct {
1174	Tier1 uint64
1175	Tier2 uint64
1176	Tier3 uint64
1177}
1178
1179// NewTierRatio constructs the reward-share ratio for tiers 1 through 3.
1180//
1181// Parameters:
1182//   - tier1: Tier-1 share scaled by 100 (for example, 70 means 70%).
1183//   - tier2: Tier-2 share scaled by 100.
1184//   - tier3: Tier-3 share scaled by 100.
1185//
1186// Returns:
1187//   - ratio: TierRatio containing the supplied scaled shares.
1188func NewTierRatio(tier1, tier2, tier3 uint64) TierRatio {
1189	return TierRatio{
1190		Tier1: tier1,
1191		Tier2: tier2,
1192		Tier3: tier3,
1193	}
1194}
1195
1196// Get returns the ratio(scaled up by 100) for the given tier.
1197//
1198// Parameters:
1199//   - tier: Tier number to query; only tiers 1, 2, and 3 are supported.
1200//
1201// Returns:
1202//   - ratio: Requested tier share scaled by 100.
1203//   - err: Non-nil when tier is not 1, 2, or 3; nil for a supported tier.
1204func (ratio *TierRatio) Get(tier uint64) (uint64, error) {
1205	switch tier {
1206	case 1:
1207		return ratio.Tier1, nil
1208	case 2:
1209		return ratio.Tier2, nil
1210	case 3:
1211		return ratio.Tier3, nil
1212	default:
1213		return 0, errors.New(ufmt.Sprintf("unsupported tier(%d)", tier))
1214	}
1215}
1216
1217// SwapBatchProcessor processes tick crosses in batch for a swap
1218// This processor accumulates all tick crosses that occur during a single swap
1219// and processes them together at the end, reducing redundant calculations
1220// and state updates that would occur with individual tick processing
1221type SwapBatchProcessor struct {
1222	poolPath  string           // The pool path identifier for this swap
1223	pool      *Pool            // Reference to the pool being swapped in
1224	crosses   []*SwapTickCross // Accumulated tick crosses during the swap
1225	timestamp int64            // Timestamp when the swap started
1226	isActive  bool             // Flag to prevent accumulation after swap ends
1227}
1228
1229// PoolPath returns the pool path associated with this swap batch.
1230//
1231// Returns:
1232//   - path: Pool identifier for the swap being processed.
1233func (s *SwapBatchProcessor) PoolPath() string {
1234	return s.poolPath
1235}
1236
1237// SetPoolPath stores the pool identifier associated with this swap batch.
1238//
1239// Parameters:
1240//   - poolPath: Pool identifier to associate with the swap batch.
1241func (s *SwapBatchProcessor) SetPoolPath(poolPath string) {
1242	s.poolPath = poolPath
1243}
1244
1245// Pool returns the pool referenced by this swap batch.
1246//
1247// Returns:
1248//   - pool: Pool state used to process the accumulated tick crosses.
1249func (s *SwapBatchProcessor) Pool() *Pool {
1250	return s.pool
1251}
1252
1253// SetPool replaces the pool reference used by this swap batch.
1254//
1255// Parameters:
1256//   - pool: Pool state to reference while processing tick crosses.
1257func (s *SwapBatchProcessor) SetPool(pool *Pool) {
1258	s.pool = pool
1259}
1260
1261// Crosses returns the tick crosses accumulated by this batch.
1262//
1263// Returns:
1264//   - crosses: Slice of tick-cross records in the order they were added.
1265func (s *SwapBatchProcessor) Crosses() []*SwapTickCross {
1266	return s.crosses
1267}
1268
1269// SetCrosses replaces the batch's accumulated tick-cross sequence.
1270//
1271// Parameters:
1272//   - crosses: Tick-cross records to store as the batch's accumulated sequence.
1273func (s *SwapBatchProcessor) SetCrosses(crosses []*SwapTickCross) {
1274	s.crosses = crosses
1275}
1276
1277// Timestamp returns the Unix timestamp in seconds recorded when the swap started.
1278//
1279// Returns:
1280//   - timestamp: Swap-start timestamp in Unix seconds.
1281func (s *SwapBatchProcessor) Timestamp() int64 {
1282	return s.timestamp
1283}
1284
1285// SetTimestamp stores the swap-start timestamp for this batch.
1286//
1287// Parameters:
1288//   - timestamp: Unix timestamp in seconds to record for the swap batch.
1289func (s *SwapBatchProcessor) SetTimestamp(timestamp int64) {
1290	s.timestamp = timestamp
1291}
1292
1293// IsActive reports the processor's stored active flag.
1294//
1295// Returns:
1296//   - active: True when the batch is marked active for the swap lifecycle.
1297func (s *SwapBatchProcessor) IsActive() bool {
1298	return s.isActive
1299}
1300
1301// SetIsActive stores the processor's active lifecycle flag.
1302//
1303// Parameters:
1304//   - isActive: Active flag to store for the swap lifecycle.
1305func (s *SwapBatchProcessor) SetIsActive(isActive bool) {
1306	s.isActive = isActive
1307}
1308
1309// LastCross returns the most recently appended tick cross.
1310//
1311// Returns:
1312//   - cross: Last tick-cross record, or nil when no crosses have been added.
1313func (s *SwapBatchProcessor) LastCross() *SwapTickCross {
1314	if len(s.crosses) == 0 {
1315		return nil
1316	}
1317
1318	return s.crosses[len(s.crosses)-1]
1319}
1320
1321// AddCross appends a tick-cross record to the batch sequence.
1322//
1323// Parameters:
1324//   - tickCross: Tick-cross record to append.
1325func (s *SwapBatchProcessor) AddCross(tickCross *SwapTickCross) {
1326	s.crosses = append(s.crosses, tickCross)
1327}
1328
1329// NewSwapBatchProcessor creates an active batch for collecting tick crosses in a pool swap.
1330//
1331// Parameters:
1332//   - poolPath: Pool identifier associated with the swap.
1333//   - pool: Pool state whose tick crosses are being collected.
1334//   - timestamp: Unix timestamp in seconds when the swap began.
1335//
1336// Returns:
1337//   - processor: Active processor with an empty cross sequence.
1338func NewSwapBatchProcessor(poolPath string, pool *Pool, timestamp int64) *SwapBatchProcessor {
1339	return &SwapBatchProcessor{
1340		poolPath:  poolPath,
1341		pool:      pool,
1342		crosses:   make([]*SwapTickCross, 0),
1343		timestamp: timestamp,
1344		isActive:  true,
1345	}
1346}
1347
1348// SwapTickCross stores information about a tick cross during a swap
1349// This struct is used to accumulate tick cross events during a single swap transaction
1350// for batch processing to optimize gas usage and computational efficiency
1351type SwapTickCross struct {
1352	tickID     int32     // The tick index that was crossed
1353	zeroForOne bool      // Direction of the swap (true: token0->token1, false: token1->token0)
1354	delta      *i256.Int // Pre-calculated liquidity delta for this tick cross
1355}
1356
1357// TickID returns the index of the crossed tick.
1358//
1359// Returns:
1360//   - tickID: Tick index represented by this cross.
1361func (s *SwapTickCross) TickID() int32 {
1362	return s.tickID
1363}
1364
1365// ZeroForOne reports the swap direction represented by this cross.
1366//
1367// Returns:
1368//   - zeroForOne: True for token0-to-token1 swaps; false for token1-to-token0 swaps.
1369func (s *SwapTickCross) ZeroForOne() bool {
1370	return s.zeroForOne
1371}
1372
1373// Delta returns the precomputed net staked-liquidity change for the crossed tick.
1374//
1375// Returns:
1376//   - delta: Signed liquidity delta to apply at the crossed tick.
1377func (s *SwapTickCross) Delta() *i256.Int {
1378	return s.delta
1379}
1380
1381// NewSwapTickCross creates a tick-cross record for a swap batch.
1382//
1383// Parameters:
1384//   - tickID: Index of the tick crossed during the swap.
1385//   - zeroForOne: Swap direction; true means token0-to-token1 and false means token1-to-token0.
1386//   - delta: Precomputed signed staked-liquidity change for this tick cross.
1387//
1388// Returns:
1389//   - cross: Tick-cross record containing the supplied index, direction, and delta.
1390func NewSwapTickCross(tickID int32, zeroForOne bool, delta *i256.Int) *SwapTickCross {
1391	return &SwapTickCross{
1392		tickID:     tickID,
1393		zeroForOne: zeroForOne,
1394		delta:      delta,
1395	}
1396}