Skip to content

Commit

Permalink
Lockup: Make release_duration compatible with vesting schedule
Browse files Browse the repository at this point in the history
  • Loading branch information
Evgeny Kuzyakov committed Aug 27, 2020
1 parent 6439d70 commit 81c3346
Show file tree
Hide file tree
Showing 10 changed files with 416 additions and 200 deletions.
231 changes: 118 additions & 113 deletions lockup/Cargo.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions lockup/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
[package]
name = "lockup-contract"
version = "0.3.0"
version = "1.0.0"
authors = ["Near Inc <hello@nearprotocol.com>"]
edition = "2018"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
near-sdk = "1.0.0"
near-sdk = "2.0.0"
uint = { version = "0.8.3", default-features = false }

[profile.release]
codegen-units = 1
# Tell `rustc` to optimize for small code size.
opt-level = "z"
opt-level = "s"
lto = true
debug = false
panic = "abort"
Expand Down
15 changes: 12 additions & 3 deletions lockup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ With the guarantees from the staking pool contracts, whitelist and voting contra

## Change Log

### `1.0.0`

- Make `release_duration` independent from the `vesting_schedule`. They are not allowed to be used simultaneously.
- Internal. Remove some JSON serialization on inner structures.

### `0.3.0`

- Introduced optional release duration
Expand Down Expand Up @@ -137,10 +142,14 @@ The initialization method has the following interface.
/// - `transfers_information` - the information about the transfers. Either transfers are
/// already enabled, then it contains the timestamp when they were enabled. Or the transfers
/// are currently disabled and it contains the account ID of the transfer poll contract.
/// - `vesting_schedule` - if present, describes the vesting schedule.
/// - `vesting_schedule` - if present, describes the vesting schedule for employees. Vesting
/// schedule affects the amount of tokens the NEAR Foundation will get in case of
/// employment termination as well as the amount of tokens available for transfer by
/// the employee.
/// - `release_duration` - is the duration when the full lockup amount will be available.
/// The tokens are linearly released from the moment transfers are enabled. It can not be
/// provided with the vesting schedule.
/// The tokens are linearly released from the moment transfers are enabled. If it's used
/// in addition to the vesting schedule, then the amount of tokens available to transfer
/// is subject to the minimum between vested tokens and released tokens.
/// - `staking_pool_whitelist_account_id` - the Account ID of the staking pool whitelist contract.
/// - `foundation_account_id` - the account ID of the NEAR Foundation, that has the ability to
/// terminate vesting schedule.
Expand Down
Binary file modified lockup/res/lockup_contract.wasm
Binary file not shown.
2 changes: 1 addition & 1 deletion lockup/src/foundation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl LockupContract {
TerminationStatus::ReadyToWithdraw
};

self.release_information = ReleaseInformation::Terminating(TerminationInformation {
self.vesting_information = VestingInformation::Terminating(TerminationInformation {
unvested_amount,
status,
});
Expand Down
12 changes: 4 additions & 8 deletions lockup/src/foundation_callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,17 +202,13 @@ impl LockupContract {
.as_bytes(),
);
// Decreasing lockup amount after withdrawal.
self.lockup_information.lockup_amount.0 = self
.lockup_information
.lockup_amount
.0
.saturating_sub(amount.0);
self.lockup_information.termination_withdrawn_tokens += amount.0;
let unvested_amount = self.get_terminated_unvested_balance().0;
if unvested_amount > amount.0 {
// There is still unvested balance remaining.
let remaining_balance = unvested_amount - amount.0;
self.release_information =
ReleaseInformation::Terminating(TerminationInformation {
self.vesting_information =
VestingInformation::Terminating(TerminationInformation {
unvested_amount: remaining_balance.into(),
status: TerminationStatus::ReadyToWithdraw,
});
Expand All @@ -224,7 +220,7 @@ impl LockupContract {
.as_bytes(),
);
} else {
self.release_information = ReleaseInformation::None;
self.vesting_information = VestingInformation::None;
self.foundation_account_id = None;
env::log(b"Vesting schedule termination and withdrawal are completed");
}
Expand Down
74 changes: 38 additions & 36 deletions lockup/src/getters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl LockupContract {

/// Returns the current termination status or `None` in case of no termination.
pub fn get_termination_status(&self) -> Option<TerminationStatus> {
if let ReleaseInformation::Terminating(termination_information) = &self.release_information
if let VestingInformation::Terminating(termination_information) = &self.vesting_information
{
Some(termination_information.status)
} else {
Expand All @@ -40,9 +40,9 @@ impl LockupContract {
/// The amount of tokens that are not going to be vested, because the vesting schedule was
/// terminated earlier.
pub fn get_terminated_unvested_balance(&self) -> WrappedBalance {
if let ReleaseInformation::Terminating(TerminationInformation {
if let VestingInformation::Terminating(TerminationInformation {
unvested_amount, ..
}) = &self.release_information
}) = &self.vesting_information
{
*unvested_amount
} else {
Expand All @@ -61,24 +61,47 @@ impl LockupContract {

/// Get the amount of tokens that are locked in this account due to lockup or vesting.
pub fn get_locked_amount(&self) -> WrappedBalance {
let lockup_amount = self.lockup_information.lockup_amount;
if let TransfersInformation::TransfersEnabled {
transfers_timestamp,
} = &self.lockup_information.transfers_information
{
let lockup_timestamp = std::cmp::max(
transfers_timestamp
.0
.saturating_add(self.lockup_information.lockup_duration.0),
self.lockup_information
.lockup_timestamp
.map_or(0, |t| t.0),
.saturating_add(self.lockup_information.lockup_duration),
self.lockup_information.lockup_timestamp.unwrap_or(0),
);
if lockup_timestamp <= env::block_timestamp() {
return self.get_unvested_amount();
let block_timestamp = env::block_timestamp();
if lockup_timestamp <= block_timestamp {
let unreleased_amount =
if let &Some(release_duration) = &self.lockup_information.release_duration {
let end_timestamp = transfers_timestamp.0.saturating_add(release_duration);
if block_timestamp >= end_timestamp {
// Everything is released
0
} else {
let time_left = U256::from(end_timestamp - block_timestamp);
let unreleased_amount = U256::from(lockup_amount) * time_left
/ U256::from(release_duration);
// The unreleased amount can't be larger than lockup_amount because the
// time_left is smaller than total_time.
unreleased_amount.as_u128()
}
} else {
0
};

return (std::cmp::max(
unreleased_amount
.saturating_sub(self.lockup_information.termination_withdrawn_tokens),
self.get_unvested_amount().0,
))
.into();
}
}
// The entire balance is still locked before the lockup timestamp.
self.lockup_information.lockup_amount
(lockup_amount - self.lockup_information.termination_withdrawn_tokens).into()
}

/// Get the amount of tokens that are already vested, but still locked due to lockup.
Expand All @@ -89,13 +112,13 @@ impl LockupContract {
/// Get the amount of tokens that are locked in this account due to vesting.
pub fn get_unvested_amount(&self) -> WrappedBalance {
let block_timestamp = env::block_timestamp();
let lockup_amount = self.lockup_information.lockup_amount.0;
match &self.release_information {
ReleaseInformation::None => {
let lockup_amount = self.lockup_information.lockup_amount;
match &self.vesting_information {
VestingInformation::None => {
// Everything is vested and unlocked
0.into()
}
ReleaseInformation::Vesting(vesting_schedule) => {
VestingInformation::Vesting(vesting_schedule) => {
if block_timestamp < vesting_schedule.cliff_timestamp.0 {
// Before the cliff, nothing is vested
lockup_amount.into()
Expand All @@ -115,28 +138,7 @@ impl LockupContract {
unvested_amount.as_u128().into()
}
}
ReleaseInformation::ReleaseDuration(release_duration) => {
if let TransfersInformation::TransfersEnabled {
transfers_timestamp,
} = &self.lockup_information.transfers_information
{
let end_timestamp = transfers_timestamp.0.saturating_add(release_duration.0);
if block_timestamp >= end_timestamp {
// After the end, everything is vested
0.into()
} else {
let time_left = U256::from(end_timestamp - block_timestamp);
let unreleased_amount =
U256::from(lockup_amount) * time_left / U256::from(release_duration.0);
// The unreleased amount can't be larger than lockup_amount because the
// time_left is smaller than total_time.
unreleased_amount.as_u128().into()
}
} else {
lockup_amount.into()
}
}
ReleaseInformation::Terminating(termination_information) => {
VestingInformation::Terminating(termination_information) => {
termination_information.unvested_amount
}
}
Expand Down
8 changes: 4 additions & 4 deletions lockup/src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ impl LockupContract {
}

pub fn set_termination_status(&mut self, status: TerminationStatus) {
if let ReleaseInformation::Terminating(termination_information) =
&mut self.release_information
if let VestingInformation::Terminating(termination_information) =
&mut self.vesting_information
{
termination_information.status = status;
} else {
Expand All @@ -31,15 +31,15 @@ impl LockupContract {
}

pub fn assert_vesting(&self) {
if let ReleaseInformation::Vesting(_) = &self.release_information {
if let VestingInformation::Vesting(_) = &self.vesting_information {
// OK
} else {
env::panic(b"There is no vesting in progress");
}
}

pub fn assert_no_termination(&self) {
if let ReleaseInformation::Terminating(_) = &self.release_information {
if let VestingInformation::Terminating(_) = &self.vesting_information {
env::panic(b"All operations are blocked until vesting termination is completed");
}
}
Expand Down
Loading

0 comments on commit 81c3346

Please sign in to comment.