Skip to main content

Collections

This segment explores key storage structures that manage various entities within the Invariant Protocol. These collections play a crucial role in organizing and managing data in a structured manner, enhancing the overall functionality and performance of our contract. Within our collection interface, we enforce a tightly defined set of operations available for all data collections. Each collection implements the same basic functionality, allowing for consistent data management regardless of the underlying data structures (mappings or subcontracts).

Positions

Abstract Contract Positions(clamm: CLAMM) extends PositionHelper(clamm) {...}

Contract Invariant(...) extends Positions(clamm), ...{
...
mapping[ByteVec, Position] positions
mapping[Address, U256] positionsCounter
...
}

The Positions Abstract Contract is designed to manage positions associated with different accounts. It uses a mapping data structure where each position is uniquely identified by the user's address and the index position within the user's position list. The provided functions allow you to add, update, remove, transfer, and retrieve positions and the number of positions associated with specific addresses. The CLAMM taken as input parameter is the address of our Concentrated Liquidity Automatic Market Maker Contract where calculations are done. This is designed as such due to the Contract bytecode size limitations.

TypeKeyValue
mapping[ByteVec, Position]ByteVec containing the user's address and the index of a position.Position struct holding the position's data.
mapping[Address, U256]User's address.Number of the user's positions.

Add Position

@using(preapprovedAssets = true)
fn wrappedAddPosition(caller: Address, payer: Address, position: Position) -> ();

Adds a new position to the caller's account.

Input parameters

NameTypeDescription
callerAddressThe address of the user who will receive the position.
payerAddressThe address of the user who will pay for creating the contract (memory allocation).
positionPositionThe Position struct with data.

Remove Position

fn wrappedRemovePosition(caller: Address, index: U256) -> ();

Removes a position at a specific index for the specified account.

Input parameters

NameTypeDescription
callerAddressThe address of the user whose position will be removed.
indexU256The index of an existing position of the user.

Transfer Position

@using(preapprovedAssets = true)
fn wrappedTransferPosition(caller: Address, index: U256, newOwner: Address) -> ();

Transfers a position from one account to another. The fee for creating the contract is covered by the transferrer.

Input parameters

NameTypeDescription
callerAddressThe address of the user whose position will be transferred.
indexU256The index of an existing position of the user.
newOwnerAddressThe address of the user who will receive the position.

Get Position

Option abstraction

The (Bool, Struct) tuple fulfils the same role as Rust's Option abstraction. Bool's state indicates whether the second parameter "is some?", in the case it is not a default value is passed and should not be used.

fn wrappedGetPosition(owner: Address, index: U256) -> (Bool, Position);

Retrieves a position at a specific index for the specified account.

Input parameters

NameTypeDescription
ownerAddressThe address of the user whose position will be returned.
indexU256The index of an existing position of the user.

Output parameters

TypeDescription
BoolIf true the position was found and retrieved successfully, false otherwise.
PositionThe user's position or an empty Position.

Get Number Of Positions

fn positionCount(caller: Address) -> U256;

Retrieves the number of positions associated with the specified account.

Input parameters

NameTypeDescription
callerAddressThe address of the user whose number of positions will be returned.

Output parameters

TypeDescription
U256The number of the user's positions.

Ticks

Abstract Contract Ticks() extends TickHelper() {...}

Contract Invariant(...) extends Ticks(), ...{
...
mapping[ByteVec, Tick] ticks
...
}

The Ticks Abstract Contract is designed to manage ticks associated between different pools. It uses a mapping data structure, where each tick is identified by a tuple of PoolKey and U256 (tick index), and a Tick object is stored as the associated value. The provided functions allow you to retrieve, add, update, and remove ticks associated with specific PoolKey values.

TypeKeyValue
mapping[ByteVec, Tick]ByteVec containing the pool key and the tick index of a tick.Tick struct holding the tick's data.

Add tick

@using(preapprovedAssets = true)
fn wrappedCreateTick(
originalCaller: Address,
poolKey: PoolKey,
index: I256,
...
) -> ();

Adds a new tick associated with a specific pool key and index.

NameTypeDescription
originalCallerAddressThe address of the user who created the position. They pay the deposit fee.
poolKeyPoolKeyPool key of the pool.
indexI256Index of a tick.
...Tick fieldsRefer to the Tick struct.

Update tick

fn rewriteTick(poolKey: PoolKey, tick: Tick) -> ();

Updates an existing tick associated with a specific pool key and index.

Input parameters

NameTypeDescription
poolKeyPoolKeyPool key of the pool.
tickTickTick struct with tick data.

Remove tick

fn removeTick(caller: Address, poolKey: PoolKey, index: I256) -> ();

Removes a tick associated with a specific pool key and index.

Input parameters

NameTypeDescription
poolKeyPoolKeyPool key of the pool.
indexI256Index of a tick.

Get tick

Option abstraction

The (Bool, Struct) tuple fulfils the same role as Rust's Option abstraction. Bool's state indicates whether the second parameter "is some?", in the case it is not a default value is passed and should not be used.

fn wrappedGetTick(poolKey: PoolKey, index: I256) -> (Bool, Tick);

Retrieves a tick associated with a specific pool key and index.

Input parameters

NameTypeDescription
poolKeyPoolKeyPool key of the pool.
indexI256Index of a tick.

Output parameters

TypeDescription
BoolIf true the tick was found and retrieved successfully, false otherwise.
TickTick struct with tick data or an empty Tick.

Pools

Abstract Contract Pools(clamm: CLAMM) extends PoolKeyHelper(), PoolHelper(clamm) {...}

Contract Invariant(...) extends Pools(CLAMM), ...{
...
mapping[ByteVec, Pool] pools
...
}

The Pools struct is designed to manage pools associated with different PoolKey values. It uses a mapping data structure, where each pool is identified by a unique PoolKey, and a Pool object is stored as the associated value. The provided functions allow you to add and retrieve pools associated with specific PoolKey values.

TypeKeyValue
mapping[PoolKey, Pool]The pool key of a specified pool.Pool struct holding the pool's data.

Add pool

@using(preapprovedAssets = true)
fn addPool(
originalCaller: Address,
poolKey: PoolKey,
...
) -> ();

Adds a new pool associated with the specified pool key. Throws an exception if a pool with the same pool key already exists.

Input parameters

NameTypeDescription
originalCallerAddressThe address of the user who created the pool. They pay the pool creation fee.
poolKeyPoolKeyPool key of the pool.
...Pool fieldsRefer to the Pool struct.

Get pool

Option abstraction

The (Bool, Struct) tuple fulfils the same role as Rust's Option abstraction. Bool's state indicates whether the second parameter "is some?", in the case it is not a default value is passed and should not be used.

fn wrappedGetPool(poolKey: PoolKey) -> (Bool, Pool);

Retrieves a pool associated with the specified pool key.

Input parameters

NameTypeDescription
poolKeyPoolKeyPool key of the pool.

Output parameters

TypeDescription
BoolIf true the pool was found and retrieved successfully, false otherwise.
PoolPool struct with pool data.

Fee Tiers

struct FeeTiers {
mut feeTiers: [FeeTier; 32]
}

Contract Invariant(mut feeTiers: FeeTiers, feeTierCount: U256, ...){
const MaxFeeTiers = 32
}

The FeeTiers struct is designed to manage fee tiers. It utilizes an array of FeeTier objects. The provided functions allow you to add, retrieve, update, and remove fee tiers within the collection. You can perform operations on these fee tiers based on their index. The current highest index is stored in the feeTierCount variable. Our protocol stores at most 32 active fee tiers.

TypeValue
[FeeTier; 32]FeeTier struct holding the fee tier's data.

Add fee tier

@using(updateFields = true, preapprovedAssets = true)
fn wrappedAddFeeTier(originalCaller: Address, feeTier: FeeTier) -> ();

Adds a new fee tier associated with the specified FeeTier. Throws an exception if fee tier already exist.

Input parameters

NameTypeDescription
originalCallerAddressAddress of the user who wants to add a FeeTier.
feeTierFeeTierFee tier you want to add.

Remove fee tier

@using(updateFields = true)
fn wrappedRemoveFeeTier(originalCaller: Address, feeTier: FeeTier) -> ();

Removes a fee tier associated with the specified FeeTier. Throws an exception if fee tier cannot be found.

Input parameters

NameTypeDescription
originalCallerAddressAddress of the user who wants to remove a FeeTier.
feeTierFeeTierFee tier you want to remove.

Contains fee tier

pub fn containsFeeTier(feeTier: FeeTier) -> Bool;

Verifies if specified fee tier exists.

Input parameters

NameTypeDescription
feeTierFeeTierFee tier you want to check if exists.

Output parameters

TypeDescription
boolBool value indicating if fee tier exists or not.

Get all fee tiers

pub fn getAllFeeTiers() -> ByteVec;

Retrieves all fee tiers.

Output parameters

TypeDescription
ByteVecA ByteVec containing all fee tiers' data.

Pool Keys

Abstract Contract PoolKeys() {...}

Contract Invariant(mut poolKeyCount: U256) extends PoolKeys(), ...{
...
mapping[U256, PoolKey] poolKeys
...
}

The PoolKeys struct is designed to manage pool keys. It utilizes a Mapping data structure, where each element corresponds to a different pool key represented by a PoolKey object. The provided functions allow you to add and retrieve pool keys within the collection. Each pool key is uniquely identified within the mapping, and you can perform operations on these pool keys based on their positions in the map.

TypeKeyValue
mapping[U256, PoolKey]Index of a specified poolKey in the map.The pool key of a specified pool.

Add pool key

@using(preapprovedAssets = true, updateFields = true)
fn addPoolKey(originalCaller: Address, poolKey: PoolKey) -> ();

Adds a new pool key. Throws an exception if pool key already exists.

Input parameters

NameTypeDescription
originalCallerAddressAddress of the user who wants to add a PoolKey.
poolKeyPoolKeyPool key you want to add.

Contains pool key

fn containsPoolKey(poolKey: PoolKey) -> Bool;

Verifies if specified pool key exist.

Input parameters

NameTypeDescription
poolKeyPoolKeyThe pool key you want to check if exists.

Output parameters

TypeDescription
boolBool value indicating if pool key exists or not.

Get all pool keys

fn getAllPoolKeys() -> ByteVec;

Retrieves all pool keys.

Output parameters

TypeDescription
ByteVecA ByteVec containing all pool keys' data.

Tickmap

Abstract Contract Tickmap() extends Decimal(), BatchHelper() {...}

Contract Invariant(...) extends Tickmap(), ...{
...
mapping[ByteVec, TickmapBatch] bitmap
...
}

The Tickmap Abstract Contract is designed to aid efficient traversal over ticks in a Pool. It utilizes a mapping data structure where each pool is identified by a PoolKey and TickmapBatch index. Due to the data storage limits of a single Contract, a Pool's tickmap is divided into several TickmapBatches and further divided into chunks. One batch consists of 94 chunks, each storing information about the initialization state of 256 ticks. The maximum size of a Tickmap is affected by the tickSpacing parameter of the FeeTier. The higher the tick spacing the less Contracts the tickmap employs.

TypeKeyValue
mapping[ByteVec, TickmapBatch]The pool key of a specified pool and TickmapBatch index.TickmapBatch struct holding the chunks' data.

Get Tick's position

pub fn tickToPosition(tick: I256, tickSpacing: U256) -> (U256, U256);

Calculates where tick's bit will be located.

Input parameters

NameTypeDescription
tickI256Index of a tick.
tickSpacingU256The spacing between initializable ticks.

Output parameters

TypeDescription
U256Index of the chunk.
U256Index of the bit in a chunk.

Get Chunk

fn getChunk(chunk: U256, poolKey: PoolKey) -> U256;

Retrieve chunk's data.

NameTypeDescription
chunkU256Index of a chunk.
poolKeypoolKeyThe pool key of a specified pool.

Output parameters

TypeDescription
U256The chunk's bits.

Flip bit utility

fn flipBitAtPosition(value: U256, position: U256) -> U256;

Flips bit in value at position.

NameTypeDescription
valueU256The value in which we flip a bit.
positionU256The position of a bit to set.

Output parameters

TypeDescription
U256value with the specified bit flipped.

Contains initialized tick

pub fn getBit(tick: I256, poolKey: PoolKey) -> Bool;

Retrieves the state of the exact bit representing the initialization state of a tick (1 - initialized, 0 - uninitialized).

Input parameters

NameTypeDescription
tickI256Index of a tick.
poolKeypoolKeyThe pool key of a specified pool.

Output parameters

TypeDescription
BoolState of the tick, true if initialized.

Reserves

Abstract Contract Reserves() {...}

Contract Invariant(...) extends Reserves(), ...{
...
mapping[ByteVec, ByteVec] reserves
...
}

The Reserves Abstract Contract is designed to overcome the challenge of being able to store a limited number of assets (tokens) in a single contract. It utilizes a mapping data structure where each token's Reserve's location is identified by the id of a given asset. The need to introduce a collection like this is due to the limited number of assets a single UTXO can store, currently 8. Each Contract has a single UTXO. We went with this design because if both tokens for a trading pair are already in a reserve users pay only for storage of trading pair specific information.

TypeKeyValue
mapping[ByteVec, ByteVec]The ContractId of a given token.The Contract ID of the reserve containing the given token.

Add reserve

@using(updateFields = true, preapprovedAssets = true)
fn initReserve(caller: Address, reservePath: ByteVec, assetsToStore: U256) -> ByteVec;

Adds a new Reserve and instantly registers assetsToStore assets.

Input parameters

NameTypeDescription
callerAddressAddress of the user who wants to create a reserve. They pay the allocation fee.
reservePathByteVecUnique identifier to be used for the new Subcontract. Usually contractId of one of the tokens.
assetsToStoreU256The number of assets that will be stored inside the reserve.

Output parameters

TypeDescription
ByteVecContractId of the Reserve.

Get both reserves

Token sorting

This function employs the token[X|Y] naming convention, indicating that arranging these tokens in ascending order by contractId is necessary.

@using(updateFields = true, preapprovedAssets = true)
fn handleReserves(caller: Address, tokenX: ByteVec, tokenY: ByteVec) -> (ByteVec, ByteVec);

Retrieves the ids of Reserves for both tokens. If a token isn't stored in a Reserve yet allocates space for it.

Input parameters

NameTypeDescription
callerAddressAddress of the user who wants to know where the Token is or will be stored. They are required to pay the eventual fee.
tokenXByteVecId of the first token.
tokenYByteVecId of the second token.

Output parameters

TypeDescription
ByteVecContractId of tokenX's reserve.
ByteVecContractId of tokenY's reserve.