Skip to main content

TwabController

Git Source

Author: PoolTogether Inc. & G9 Software Inc.

This TwabController uses the TwabLib to provide token balances and on-chain historical lookups to a user(s) time-weighted average balance. Each user is mapped to an Account struct containing the TWAB history (ring buffer) and ring buffer parameters. Every token.transfer() creates a new TWAB observation. The new TWAB observation is stored in the circular ring buffer as either a new observation or rewriting a previous observation with new parameters. One observation per period is stored. The TwabLib guarantees minimum 1 year of search history if a period is a day.

Time-Weighted Average Balance Controller for ERC20 tokens.

Constants

MINIMUM_PERIOD_LENGTH

uint32 constant MINIMUM_PERIOD_LENGTH = 1 hours;

SPONSORSHIP_ADDRESS

address constant SPONSORSHIP_ADDRESS = address(1);

State Variables

PERIOD_LENGTH

Sets the minimum period length for Observations. When a period elapses, a new Observation is recorded, otherwise the most recent Observation is updated.

uint32 public immutable PERIOD_LENGTH;

PERIOD_OFFSET

Sets the beginning timestamp for the first period. This allows us to maximize storage as well as line up periods with a chosen timestamp.

Ensure that the PERIOD_OFFSET is in the past.

uint32 public immutable PERIOD_OFFSET;

userObservations

Record of token holders TWABs for each account for each vault.

mapping(address => mapping(address => TwabLib.Account)) internal userObservations;

totalSupplyObservations

Record of tickets total supply and ring buff parameters used for observation.

mapping(address => TwabLib.Account) internal totalSupplyObservations;

delegates

vault => user => delegate.

mapping(address => mapping(address => address)) internal delegates;

Functions

constructor

Construct a new TwabController.

Reverts if the period offset is in the future.

constructor(uint32 _periodLength, uint32 _periodOffset);

Parameters

NameTypeDescription
_periodLengthuint32Sets the minimum period length for Observations. When a period elapses, a new Observation is recorded, otherwise the most recent Observation is updated.
_periodOffsetuint32Sets the beginning timestamp for the first period. This allows us to maximize storage as well as line up periods with a chosen timestamp.

isShutdownAt

Returns whether the TwabController has been shutdown at the given timestamp If the twab is queried at or after this time, whether an absolute timestamp or time range, it will return 0.

This function will return true for any timestamp after the lastObservationAt()

function isShutdownAt(uint256 timestamp) external view returns (bool);

Parameters

NameTypeDescription
timestampuint256The timestamp to check

Returns

NameTypeDescription
<none>boolTrue if the TwabController is shutdown at the given timestamp, false otherwise.

lastObservationAt

Computes the timestamp after which no more observations will be made.

function lastObservationAt() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The largest timestamp at which the TwabController can record a new observation.

getAccount

Loads the current TWAB Account data for a specific vault stored for a user.

Note this is a very expensive function

function getAccount(address vault, address user) external view returns (TwabLib.Account memory);

Parameters

NameTypeDescription
vaultaddressthe vault for which the data is being queried
useraddressthe user whose data is being queried

Returns

NameTypeDescription
<none>TwabLib.AccountThe current TWAB Account data of the user

getTotalSupplyAccount

Loads the current total supply TWAB Account data for a specific vault.

Note this is a very expensive function

function getTotalSupplyAccount(address vault) external view returns (TwabLib.Account memory);

Parameters

NameTypeDescription
vaultaddressthe vault for which the data is being queried

Returns

NameTypeDescription
<none>TwabLib.AccountThe current total supply TWAB Account data

balanceOf

The current token balance of a user for a specific vault.

function balanceOf(address vault, address user) external view returns (uint256);

Parameters

NameTypeDescription
vaultaddressthe vault for which the balance is being queried
useraddressthe user whose balance is being queried

Returns

NameTypeDescription
<none>uint256The current token balance of the user

totalSupply

The total supply of tokens for a vault.

function totalSupply(address vault) external view returns (uint256);

Parameters

NameTypeDescription
vaultaddressthe vault for which the total supply is being queried

Returns

NameTypeDescription
<none>uint256The total supply of tokens for a vault

totalSupplyDelegateBalance

The total delegated amount of tokens for a vault.

Delegated balance is not 1:1 with the token total supply. Users may delegate their balance to the sponsorship address, which will result in those tokens being subtracted from the total.

function totalSupplyDelegateBalance(address vault) external view returns (uint256);

Parameters

NameTypeDescription
vaultaddressthe vault for which the total delegated supply is being queried

Returns

NameTypeDescription
<none>uint256The total delegated amount of tokens for a vault

delegateOf

The current delegate of a user for a specific vault.

function delegateOf(address vault, address user) external view returns (address);

Parameters

NameTypeDescription
vaultaddressthe vault for which the delegate balance is being queried
useraddressthe user whose delegate balance is being queried

Returns

NameTypeDescription
<none>addressThe current delegate balance of the user

delegateBalanceOf

The current delegateBalance of a user for a specific vault.

the delegateBalance is the sum of delegated balance to this user

function delegateBalanceOf(address vault, address user) external view returns (uint256);

Parameters

NameTypeDescription
vaultaddressthe vault for which the delegateBalance is being queried
useraddressthe user whose delegateBalance is being queried

Returns

NameTypeDescription
<none>uint256The current delegateBalance of the user

getBalanceAt

Looks up a users balance at a specific time in the past.

function getBalanceAt(address vault, address user, uint256 periodEndOnOrAfterTime) external view returns (uint256);

Parameters

NameTypeDescription
vaultaddressthe vault for which the balance is being queried
useraddressthe user whose balance is being queried
periodEndOnOrAfterTimeuint256The time in the past for which the balance is being queried. The time will be snapped to a period end time on or after the timestamp.

Returns

NameTypeDescription
<none>uint256The balance of the user at the target time

getTotalSupplyAt

Looks up the total supply at a specific time in the past.

function getTotalSupplyAt(address vault, uint256 periodEndOnOrAfterTime) external view returns (uint256);

Parameters

NameTypeDescription
vaultaddressthe vault for which the total supply is being queried
periodEndOnOrAfterTimeuint256The time in the past for which the balance is being queried. The time will be snapped to a period end time on or after the timestamp.

Returns

NameTypeDescription
<none>uint256The total supply at the target time

getTwabBetween

Looks up the average balance of a user between two timestamps.

Timestamps are Unix timestamps denominated in seconds

function getTwabBetween(address vault, address user, uint256 startTime, uint256 endTime)
external
view
returns (uint256);

Parameters

NameTypeDescription
vaultaddressthe vault for which the average balance is being queried
useraddressthe user whose average balance is being queried
startTimeuint256the start of the time range for which the average balance is being queried. The time will be snapped to a period end time on or after the timestamp.
endTimeuint256the end of the time range for which the average balance is being queried. The time will be snapped to a period end time on or after the timestamp.

Returns

NameTypeDescription
<none>uint256The average balance of the user between the two timestamps

getTotalSupplyTwabBetween

Looks up the average total supply between two timestamps.

Timestamps are Unix timestamps denominated in seconds

function getTotalSupplyTwabBetween(address vault, uint256 startTime, uint256 endTime) external view returns (uint256);

Parameters

NameTypeDescription
vaultaddressthe vault for which the average total supply is being queried
startTimeuint256the start of the time range for which the average total supply is being queried
endTimeuint256the end of the time range for which the average total supply is being queried

Returns

NameTypeDescription
<none>uint256The average total supply between the two timestamps

periodEndOnOrAfter

Computes the period end timestamp on or after the given timestamp.

function periodEndOnOrAfter(uint256 _timestamp) external view returns (uint256);

Parameters

NameTypeDescription
_timestampuint256The timestamp to check

Returns

NameTypeDescription
<none>uint256The end timestamp of the period that ends on or immediately after the given timestamp

_periodEndOnOrAfter

Computes the period end timestamp on or after the given timestamp.

function _periodEndOnOrAfter(uint256 _timestamp) internal view returns (uint256);

Parameters

NameTypeDescription
_timestampuint256The timestamp to compute the period end time for

Returns

NameTypeDescription
<none>uint256A period end time.

getNewestObservation

Looks up the newest observation for a user.

function getNewestObservation(address vault, address user)
external
view
returns (uint16, ObservationLib.Observation memory);

Parameters

NameTypeDescription
vaultaddressthe vault for which the observation is being queried
useraddressthe user whose observation is being queried

Returns

NameTypeDescription
<none>uint16index The index of the observation
<none>ObservationLib.Observationobservation The observation of the user

getOldestObservation

Looks up the oldest observation for a user.

function getOldestObservation(address vault, address user)
external
view
returns (uint16, ObservationLib.Observation memory);

Parameters

NameTypeDescription
vaultaddressthe vault for which the observation is being queried
useraddressthe user whose observation is being queried

Returns

NameTypeDescription
<none>uint16index The index of the observation
<none>ObservationLib.Observationobservation The observation of the user

getNewestTotalSupplyObservation

Looks up the newest total supply observation for a vault.

function getNewestTotalSupplyObservation(address vault)
external
view
returns (uint16, ObservationLib.Observation memory);

Parameters

NameTypeDescription
vaultaddressthe vault for which the observation is being queried

Returns

NameTypeDescription
<none>uint16index The index of the observation
<none>ObservationLib.Observationobservation The total supply observation

getOldestTotalSupplyObservation

Looks up the oldest total supply observation for a vault.

function getOldestTotalSupplyObservation(address vault)
external
view
returns (uint16, ObservationLib.Observation memory);

Parameters

NameTypeDescription
vaultaddressthe vault for which the observation is being queried

Returns

NameTypeDescription
<none>uint16index The index of the observation
<none>ObservationLib.Observationobservation The total supply observation

getTimestampPeriod

Calculates the period a timestamp falls into.

function getTimestampPeriod(uint256 time) external view returns (uint256);

Parameters

NameTypeDescription
timeuint256The timestamp to check

Returns

NameTypeDescription
<none>uint256period The period the timestamp falls into

hasFinalized

Checks if the given timestamp is before the current overwrite period.

function hasFinalized(uint256 time) external view returns (bool);

Parameters

NameTypeDescription
timeuint256The timestamp to check

Returns

NameTypeDescription
<none>boolTrue if the given time is finalized, false if it's during the current overwrite period.

currentOverwritePeriodStartedAt

Computes the timestamp at which the current overwrite period started.

The overwrite period is the period during which observations are collated.

function currentOverwritePeriodStartedAt() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256period The timestamp at which the current overwrite period started.

mint

Mints new balance and delegateBalance for a given user.

Note that if the provided user to mint to is delegating that the delegate's delegateBalance will be updated.

Mint is expected to be called by the Vault.

function mint(address _to, uint96 _amount) external;

Parameters

NameTypeDescription
_toaddressThe address to mint balance and delegateBalance to
_amountuint96The amount to mint

burn

Burns balance and delegateBalance for a given user.

Note that if the provided user to burn from is delegating that the delegate's delegateBalance will be updated.

Burn is expected to be called by the Vault.

function burn(address _from, uint96 _amount) external;

Parameters

NameTypeDescription
_fromaddressThe address to burn balance and delegateBalance from
_amountuint96The amount to burn

transfer

Transfers balance and delegateBalance from a given user.

Note that if the provided user to transfer from is delegating that the delegate's delegateBalance will be updated.

function transfer(address _from, address _to, uint96 _amount) external;

Parameters

NameTypeDescription
_fromaddressThe address to transfer the balance and delegateBalance from
_toaddressThe address to transfer balance and delegateBalance to
_amountuint96The amount to transfer

delegate

Sets a delegate for a user which forwards the delegateBalance tied to the user's balance to the delegate's delegateBalance.

function delegate(address _vault, address _to) external;

Parameters

NameTypeDescription
_vaultaddressThe vault for which the delegate is being set
_toaddressthe address to delegate to

Delegate user balance to the sponsorship address.

Must only be called by the Vault contract.

function sponsor(address _from) external;

Parameters

NameTypeDescription
_fromaddressAddress of the user delegating their balance to the sponsorship address.

_transferBalance

Transfers a user's vault balance from one address to another.

If the user is delegating, their delegate's delegateBalance is also updated.

If we are minting or burning tokens then the total supply is also updated.

function _transferBalance(address _vault, address _from, address _to, uint96 _amount) internal;

Parameters

NameTypeDescription
_vaultaddressthe vault for which the balance is being transferred
_fromaddressthe address from which the balance is being transferred
_toaddressthe address to which the balance is being transferred
_amountuint96the amount of balance being transferred

_delegateOf

Looks up the delegate of a user.

function _delegateOf(address _vault, address _user) internal view returns (address);

Parameters

NameTypeDescription
_vaultaddressthe vault for which the user's delegate is being queried
_useraddressthe address to query the delegate of

Returns

NameTypeDescription
<none>addressThe address of the user's delegate

_transferDelegateBalance

Transfers a user's vault delegateBalance from one address to another.

function _transferDelegateBalance(address _vault, address _fromDelegate, address _toDelegate, uint96 _amount)
internal;

Parameters

NameTypeDescription
_vaultaddressthe vault for which the delegateBalance is being transferred
_fromDelegateaddressthe address from which the delegateBalance is being transferred
_toDelegateaddressthe address to which the delegateBalance is being transferred
_amountuint96the amount of delegateBalance being transferred

_delegate

Sets a delegate for a user which forwards the delegateBalance tied to the user's balance to the delegate's delegateBalance. "Sponsoring" means the funds aren't delegated to anyone; this can be done by passing address(0) or the SPONSORSHIP_ADDRESS as the delegate.

function _delegate(address _vault, address _from, address _to) internal;

Parameters

NameTypeDescription
_vaultaddressThe vault for which the delegate is being set
_fromaddressthe address to delegate from
_toaddressthe address to delegate to

_increaseBalances

Increases a user's balance and delegateBalance for a specific vault.

function _increaseBalances(address _vault, address _user, uint96 _amount, uint96 _delegateAmount) internal;

Parameters

NameTypeDescription
_vaultaddressthe vault for which the balance is being increased
_useraddressthe address of the user whose balance is being increased
_amountuint96the amount of balance being increased
_delegateAmountuint96the amount of delegateBalance being increased

_decreaseBalances

Decreases the a user's balance and delegateBalance for a specific vault.

function _decreaseBalances(address _vault, address _user, uint96 _amount, uint96 _delegateAmount) internal;

Parameters

NameTypeDescription
_vaultaddressthe vault for which the totalSupply balance is being decreased
_useraddress
_amountuint96the amount of balance being decreased
_delegateAmountuint96the amount of delegateBalance being decreased

_decreaseTotalSupplyBalances

Decreases the totalSupply balance and delegateBalance for a specific vault.

function _decreaseTotalSupplyBalances(address _vault, uint96 _amount, uint96 _delegateAmount) internal;

Parameters

NameTypeDescription
_vaultaddressthe vault for which the totalSupply balance is being decreased
_amountuint96the amount of balance being decreased
_delegateAmountuint96the amount of delegateBalance being decreased

_increaseTotalSupplyBalances

Increases the totalSupply balance and delegateBalance for a specific vault.

function _increaseTotalSupplyBalances(address _vault, uint96 _amount, uint96 _delegateAmount) internal;

Parameters

NameTypeDescription
_vaultaddressthe vault for which the totalSupply balance is being increased
_amountuint96the amount of balance being increased
_delegateAmountuint96the amount of delegateBalance being increased

Events

IncreasedBalance

Emitted when a balance or delegateBalance is increased.

event IncreasedBalance(address indexed vault, address indexed user, uint96 amount, uint96 delegateAmount);

Parameters

NameTypeDescription
vaultaddressthe vault for which the balance increased
useraddressthe users whose balance increased
amountuint96the amount the balance increased by
delegateAmountuint96the amount the delegateBalance increased by

DecreasedBalance

Emitted when a balance or delegateBalance is decreased.

event DecreasedBalance(address indexed vault, address indexed user, uint96 amount, uint96 delegateAmount);

Parameters

NameTypeDescription
vaultaddressthe vault for which the balance decreased
useraddressthe users whose balance decreased
amountuint96the amount the balance decreased by
delegateAmountuint96the amount the delegateBalance decreased by

ObservationRecorded

Emitted when an Observation is recorded to the Ring Buffer.

event ObservationRecorded(
address indexed vault,
address indexed user,
uint96 balance,
uint96 delegateBalance,
bool isNew,
ObservationLib.Observation observation
);

Parameters

NameTypeDescription
vaultaddressthe vault for which the Observation was recorded
useraddressthe users whose Observation was recorded
balanceuint96the resulting balance
delegateBalanceuint96the resulting delegated balance
isNewboolwhether the observation is new or not
observationObservationLib.Observationthe observation that was created or updated

Delegated

Emitted when a user delegates their balance to another address.

event Delegated(address indexed vault, address indexed delegator, address indexed delegate);

Parameters

NameTypeDescription
vaultaddressthe vault for which the balance was delegated
delegatoraddressthe user who delegated their balance
delegateaddressthe user who received the delegated balance

IncreasedTotalSupply

Emitted when the total supply or delegateTotalSupply is increased.

event IncreasedTotalSupply(address indexed vault, uint96 amount, uint96 delegateAmount);

Parameters

NameTypeDescription
vaultaddressthe vault for which the total supply increased
amountuint96the amount the total supply increased by
delegateAmountuint96the amount the delegateTotalSupply increased by

DecreasedTotalSupply

Emitted when the total supply or delegateTotalSupply is decreased.

event DecreasedTotalSupply(address indexed vault, uint96 amount, uint96 delegateAmount);

Parameters

NameTypeDescription
vaultaddressthe vault for which the total supply decreased
amountuint96the amount the total supply decreased by
delegateAmountuint96the amount the delegateTotalSupply decreased by

TotalSupplyObservationRecorded

Emitted when a Total Supply Observation is recorded to the Ring Buffer.

event TotalSupplyObservationRecorded(
address indexed vault, uint96 balance, uint96 delegateBalance, bool isNew, ObservationLib.Observation observation
);

Parameters

NameTypeDescription
vaultaddressthe vault for which the Observation was recorded
balanceuint96the resulting balance
delegateBalanceuint96the resulting delegated balance
isNewboolwhether the observation is new or not
observationObservationLib.Observationthe observation that was created or updated