Skip to main content

Module 0x3::timelocked_staking

use 0x1::option; use 0x1::string; use 0x1::vector; use 0x2::balance; use 0x2::coin; use 0x2::iota; use 0x2::object; use 0x2::system_admin_cap; use 0x2::timelock; use 0x2::transfer; use 0x2::tx_context; use 0x3::iota_system; use 0x3::staking_pool; use 0x3::validator;

Resource TimelockedStakedIota

A self-custodial object holding the timelocked staked IOTA tokens.

struct TimelockedStakedIota has key

Fields
id: object::UID
staked_iota: staking_pool::StakedIota

A self-custodial object holding the staked IOTA tokens.

expiration_timestamp_ms: u64

This is the epoch time stamp of when the lock expires.

label: option::Option<string::String>

Timelock related label.

Constants

Incompatible objects when joining TimelockedStakedIota

const EIncompatibleTimelockedStakedIota: u64 = 1;

For when trying to stake an expired time-locked balance.

const ETimeLockShouldNotBeExpired: u64 = 0;

Function request_add_stake

Add a time-locked stake to a validator's staking pool.

public entry fun request_add_stake(iota_system: &mut iota_system::IotaSystemState, timelocked_balance: timelock::TimeLock<balance::Balance<iota::IOTA>>, validator_address: address, ctx: &mut tx_context::TxContext)

Implementation

public entry fun request_add_stake( iota_system: &mut IotaSystemState, timelocked_balance: TimeLock<Balance<IOTA>>, validator_address: address, ctx: &mut TxContext, ) { // Stake the time-locked balance. let timelocked_staked_iota = request_add_stake_non_entry(iota_system, timelocked_balance, validator_address, ctx);

// Transfer the receipt to the sender. timelocked_staked_iota.transfer_to_sender(ctx); }

Function request_add_stake_mul_bal

Add a time-locked stake to a validator's staking pool using multiple time-locked balances.

public entry fun request_add_stake_mul_bal(iota_system: &mut iota_system::IotaSystemState, timelocked_balances: vector<timelock::TimeLock<balance::Balance<iota::IOTA>>>, validator_address: address, ctx: &mut tx_context::TxContext)

Implementation

public entry fun request_add_stake_mul_bal( iota_system: &mut IotaSystemState, timelocked_balances: vector<TimeLock<Balance<IOTA>>>, validator_address: address, ctx: &mut TxContext, ) { // Stake the time-locked balances. let mut receipts = request_add_stake_mul_bal_non_entry(iota_system, timelocked_balances, validator_address, ctx);

// Create useful variables. let (mut i, len) = (0, receipts.length());

// Send all the receipts to the sender. while (i < len) { // Take a receipt. let receipt = receipts.pop_back();

// Transfer the receipt to the sender. receipt.transfer_to_sender(ctx);

i = i + 1 };

// Destroy the empty vector. vector::destroy_empty(receipts) }

Function request_withdraw_stake

Withdraw a time-locked stake from a validator's staking pool.

public entry fun request_withdraw_stake(iota_system: &mut iota_system::IotaSystemState, timelocked_staked_iota: timelocked_staking::TimelockedStakedIota, ctx: &mut tx_context::TxContext)

Implementation

public entry fun request_withdraw_stake( iota_system: &mut IotaSystemState, timelocked_staked_iota: TimelockedStakedIota, ctx: &mut TxContext, ) { // Withdraw the time-locked balance. let (timelocked_balance, reward) = request_withdraw_stake_non_entry(iota_system, timelocked_staked_iota, ctx);

// Transfer the withdrawn time-locked balance to the sender. timelocked_balance.transfer_to_sender(ctx);

// Send coins only if the reward is not zero. if (reward.value() > 0) { transfer::public_transfer(reward.into_coin(ctx), ctx.sender()); } else { balance::destroy_zero(reward); } }

Function request_add_stake_non_entry

The non-entry version of request_add_stake, which returns the time-locked staked IOTA instead of transferring it to the sender.

public fun request_add_stake_non_entry(iota_system: &mut iota_system::IotaSystemState, timelocked_balance: timelock::TimeLock<balance::Balance<iota::IOTA>>, validator_address: address, ctx: &mut tx_context::TxContext): timelocked_staking::TimelockedStakedIota

Implementation

public fun request_add_stake_non_entry( iota_system: &mut IotaSystemState, timelocked_balance: TimeLock<Balance<IOTA>>, validator_address: address, ctx: &mut TxContext, ) : TimelockedStakedIota { // Check the preconditions. assert!(timelocked_balance.is_locked(ctx), ETimeLockShouldNotBeExpired);

// Unpack the time-locked balance. let sys_admin_cap = iota_system.load_iota_system_admin_cap(); let (balance, expiration_timestamp_ms, label) = timelock::system_unpack(sys_admin_cap, timelocked_balance);

// Stake the time-locked balance. let staked_iota = iota_system.request_add_stake_non_entry( balance.into_coin(ctx), validator_address, ctx, );

// Create and return a receipt. TimelockedStakedIota { id: object::new(ctx), staked_iota, expiration_timestamp_ms, label, } }

Function request_add_stake_mul_bal_non_entry

The non-entry version of request_add_stake_mul_bal, which returns a list of the time-locked staked IOTAs instead of transferring them to the sender.

public fun request_add_stake_mul_bal_non_entry(iota_system: &mut iota_system::IotaSystemState, timelocked_balances: vector<timelock::TimeLock<balance::Balance<iota::IOTA>>>, validator_address: address, ctx: &mut tx_context::TxContext): vector<timelocked_staking::TimelockedStakedIota>

Implementation

public fun request_add_stake_mul_bal_non_entry( iota_system: &mut IotaSystemState, mut timelocked_balances: vector<TimeLock<Balance<IOTA>>>, validator_address: address, ctx: &mut TxContext, ) : vector<TimelockedStakedIota> { // Create a vector to store the results. let mut result = vector[];

// Create useful variables. let (mut i, len) = (0, timelocked_balances.length());

// Stake all the time-locked balances. while (i < len) { // Take a time-locked balance. let timelocked_balance = timelocked_balances.pop_back();

// Stake the time-locked balance. let timelocked_staked_iota = request_add_stake_non_entry(iota_system, timelocked_balance, validator_address, ctx);

// Store the created receipt. result.push_back(timelocked_staked_iota);

i = i + 1 };

// Destroy the empty vector. vector::destroy_empty(timelocked_balances);

result }

Function request_withdraw_stake_non_entry

Non-entry version of request_withdraw_stake that returns the withdrawn time-locked IOTA and reward instead of transferring it to the sender.

public fun request_withdraw_stake_non_entry(iota_system: &mut iota_system::IotaSystemState, timelocked_staked_iota: timelocked_staking::TimelockedStakedIota, ctx: &mut tx_context::TxContext): (timelock::TimeLock<balance::Balance<iota::IOTA>>, balance::Balance<iota::IOTA>)

Implementation

public fun request_withdraw_stake_non_entry( iota_system: &mut IotaSystemState, timelocked_staked_iota: TimelockedStakedIota, ctx: &mut TxContext, ) : (TimeLock<Balance<IOTA>>, Balance<IOTA>) { // Unpack the <Link to="timelocked_staking#0x3_timelocked_staking_TimelockedStakedIota">TimelockedStakedIota</Link> instance. let (staked_iota, expiration_timestamp_ms, label) = timelocked_staked_iota.unpack();

// Store the original stake amount. let principal = staked_iota.staked_iota_amount();

// Withdraw the balance. let mut withdraw_stake = iota_system.request_withdraw_stake_non_entry(staked_iota, ctx);

// The iota_system withdraw functions return a balance that consists of the original staked amount plus the reward amount; // In here, it splits the original staked balance to timelock it again. let principal = withdraw_stake.split(principal);

// Pack and return a time-locked balance, and the reward. let sys_admin_cap = iota_system.load_iota_system_admin_cap(); (timelock::system_pack(sys_admin_cap, principal, expiration_timestamp_ms, label, ctx), withdraw_stake) }

Function split

Split TimelockedStakedIota into two parts, one with principal split_amount, and the remaining principal is left in self. All the other parameters of the TimelockedStakedIota like stake_activation_epoch or pool_id remain the same.

public fun split(self: &mut timelocked_staking::TimelockedStakedIota, split_amount: u64, ctx: &mut tx_context::TxContext): timelocked_staking::TimelockedStakedIota

Implementation

public fun split(self: &mut TimelockedStakedIota, split_amount: u64, ctx: &mut TxContext): TimelockedStakedIota { let split_stake = self.staked_iota.split(split_amount, ctx);

TimelockedStakedIota { id: object::new(ctx), staked_iota: split_stake, expiration_timestamp_ms: self.expiration_timestamp_ms, label: self.label, } }

Function split_staked_iota

Split the given TimelockedStakedIota to the two parts, one with principal split_amount, transfer the newly split part to the sender address.

public entry fun split_staked_iota(stake: &mut timelocked_staking::TimelockedStakedIota, split_amount: u64, ctx: &mut tx_context::TxContext)

Implementation

public entry fun split_staked_iota(stake: &mut TimelockedStakedIota, split_amount: u64, ctx: &mut TxContext) { split(stake, split_amount, ctx).transfer_to_sender(ctx); }

Function join_staked_iota

Consume the staked iota other and add its value to self. Aborts if some of the staking parameters are incompatible (pool id, stake activation epoch, etc.)

public entry fun join_staked_iota(self: &mut timelocked_staking::TimelockedStakedIota, other: timelocked_staking::TimelockedStakedIota)

Implementation

public entry fun join_staked_iota(self: &mut TimelockedStakedIota, other: TimelockedStakedIota) { assert!(self.is_equal_staking_metadata(&other), EIncompatibleTimelockedStakedIota);

let TimelockedStakedIota { id, staked_iota, expiration_timestamp_ms: _, label: _, } = other;

id.delete();

self.staked_iota.join(staked_iota); }

Function transfer_to_sender

A utility function to transfer a TimelockedStakedIota.

public fun transfer_to_sender(stake: timelocked_staking::TimelockedStakedIota, ctx: &tx_context::TxContext)

Implementation

public fun transfer_to_sender(stake: TimelockedStakedIota, ctx: &TxContext) { transfer(stake, ctx.sender()) }

Function transfer_to_sender_multiple

A utility function to transfer multiple TimelockedStakedIota.

public fun transfer_to_sender_multiple(stakes: vector<timelocked_staking::TimelockedStakedIota>, ctx: &tx_context::TxContext)

Implementation

public fun transfer_to_sender_multiple(stakes: vector<TimelockedStakedIota>, ctx: &TxContext) { transfer_multiple(stakes, ctx.sender()) }

Function is_equal_staking_metadata

A utility function that returns true if all the staking parameters of the staked iota except the principal are identical

public fun is_equal_staking_metadata(self: &timelocked_staking::TimelockedStakedIota, other: &timelocked_staking::TimelockedStakedIota): bool

Implementation

public fun is_equal_staking_metadata(self: &TimelockedStakedIota, other: &TimelockedStakedIota): bool { self.staked_iota.is_equal_staking_metadata(&other.staked_iota) && (self.expiration_timestamp_ms == other.expiration_timestamp_ms) && (self.label() == other.label()) }

Function pool_id

Function to get the pool id of a TimelockedStakedIota.

public fun pool_id(self: &timelocked_staking::TimelockedStakedIota): object::ID

Implementation

public fun pool_id(self: &TimelockedStakedIota): ID { self.staked_iota.pool_id() }

Function staked_iota_amount

Function to get the staked iota amount of a TimelockedStakedIota.

public fun staked_iota_amount(self: &timelocked_staking::TimelockedStakedIota): u64

Implementation

public fun staked_iota_amount(self: &TimelockedStakedIota): u64 { self.staked_iota.staked_iota_amount() }

Function stake_activation_epoch

Function to get the stake activation epoch of a TimelockedStakedIota.

public fun stake_activation_epoch(self: &timelocked_staking::TimelockedStakedIota): u64

Implementation

public fun stake_activation_epoch(self: &TimelockedStakedIota): u64 { self.staked_iota.stake_activation_epoch() }

Function expiration_timestamp_ms

Function to get the expiration timestamp of a TimelockedStakedIota.

public fun expiration_timestamp_ms(self: &timelocked_staking::TimelockedStakedIota): u64

Implementation

public fun expiration_timestamp_ms(self: &TimelockedStakedIota): u64 { self.expiration_timestamp_ms }

Function label

Function to get the label of a TimelockedStakedIota.

public fun label(self: &timelocked_staking::TimelockedStakedIota): option::Option<string::String>

Implementation

public fun label(self: &TimelockedStakedIota): Option<String> { self.label }

Function is_labeled_with

Check if a TimelockedStakedIota is labeled with the type L.

public fun is_labeled_with<L>(self: &timelocked_staking::TimelockedStakedIota): bool

Implementation

public fun is_labeled_with<L>(self: &TimelockedStakedIota): bool { if (self.label.is_some()) { self.label.borrow() == timelock::type_name<L>() } else { false } }

Function unpack

A utility function to destroy a TimelockedStakedIota.

fun unpack(self: timelocked_staking::TimelockedStakedIota): (staking_pool::StakedIota, u64, option::Option<string::String>)

Implementation

fun unpack(self: TimelockedStakedIota): (StakedIota, u64, Option<String>) { let TimelockedStakedIota { id, staked_iota, expiration_timestamp_ms, label, } = self;

object::delete(id);

(staked_iota, expiration_timestamp_ms, label) }

Function transfer

A utility function to transfer a TimelockedStakedIota to a receiver.

fun transfer(stake: timelocked_staking::TimelockedStakedIota, receiver: address)

Implementation

fun transfer(stake: TimelockedStakedIota, receiver: address) { transfer::transfer(stake, receiver); }

Function transfer_multiple

A utility function to transfer a vector of TimelockedStakedIota to a receiver.

fun transfer_multiple(stakes: vector<timelocked_staking::TimelockedStakedIota>, receiver: address)

Implementation

fun transfer_multiple(mut stakes: vector<TimelockedStakedIota>, receiver: address) { // Transfer all the time-locked stakes to the recipient. while (!stakes.is_empty()) { let stake = stakes.pop_back(); transfer::transfer(stake, receiver); };

// Destroy the empty vector. vector::destroy_empty(stakes); }

Function request_add_stake_at_genesis

Request to add timelocked stake to the validator's staking pool at genesis

public(friend) fun request_add_stake_at_genesis(validator: &mut validator::ValidatorV1, stake: balance::Balance<iota::IOTA>, staker_address: address, expiration_timestamp_ms: u64, label: option::Option<string::String>, ctx: &mut tx_context::TxContext)

Implementation

public(package) fun request_add_stake_at_genesis( validator: &mut ValidatorV1, stake: Balance<IOTA>, staker_address: address, expiration_timestamp_ms: u64, label: Option<String>, ctx: &mut TxContext, ) { let staked_iota = validator.request_add_stake_at_genesis_with_receipt(stake, ctx); let timelocked_staked_iota = TimelockedStakedIota { id: object::new(ctx), staked_iota, expiration_timestamp_ms, label, }; transfer(timelocked_staked_iota, staker_address); }