Skip to content

Commit 7320741

Browse files
committed
test(lockup): add test of unlocking schedule with termination
1 parent 56972b1 commit 7320741

File tree

1 file changed

+261
-0
lines changed

1 file changed

+261
-0
lines changed

lockup/src/lib.rs

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,6 +1662,267 @@ mod tests {
16621662
);
16631663
}
16641664

1665+
#[test]
1666+
fn test_unlocking_schedule_with_termination() {
1667+
// https://wiki.near.org/getting-started/near-token/lockups#termination-of-vesting
1668+
// This test checks that termination of vesting
1669+
// fixes the amount of tokens that finally should become liquid (*),
1670+
// but does not change the schedule of unlocking the tokens.
1671+
// The contract should act as there was no termination at all,
1672+
// until we do not reach the amount (*).
1673+
// After we reach this amount, the unlocking process should stop.
1674+
1675+
// Taking bigger lockup amount because we compare the balances further
1676+
// and there is a comparison delta.
1677+
// We want to be sure that this delta does not grow on bigger numbers
1678+
let lockup_amount = to_yocto(LOCKUP_NEAR * 1000);
1679+
let mut context = basic_context();
1680+
context.account_balance = lockup_amount;
1681+
testing_env!(context.clone());
1682+
1683+
let vesting_cliff_offset = 100;
1684+
let vesting_schedule = new_vesting_schedule(vesting_cliff_offset);
1685+
let mut contract = new_contract(
1686+
true,
1687+
Some(vesting_schedule.clone()),
1688+
Some(to_nanos(YEAR * 4).into()),
1689+
true,
1690+
);
1691+
1692+
// Vesting starts at day 235
1693+
let ts_vesting_started = to_ts(GENESIS_TIME_IN_DAYS - YEAR + vesting_cliff_offset);
1694+
assert_eq!(vesting_schedule.start_timestamp.0, ts_vesting_started);
1695+
1696+
// We don't use lockup_timestamp,
1697+
// it means that lockup starts from the transfers enabled moment
1698+
assert_eq!(contract.lockup_information.lockup_timestamp, None);
1699+
1700+
// Transfers are enabled at day 500, lockup also starts here
1701+
if let TransfersInformation::TransfersEnabled {
1702+
transfers_timestamp
1703+
} = &contract.lockup_information.transfers_information
1704+
{
1705+
assert_eq!(transfers_timestamp.0, to_ts(GENESIS_TIME_IN_DAYS));
1706+
} else {
1707+
assert!(false, "Transfers should be enabled");
1708+
}
1709+
1710+
// Vesting cliff ends at day 600
1711+
assert_eq!(vesting_schedule.cliff_timestamp.0, to_ts(GENESIS_TIME_IN_DAYS + vesting_cliff_offset));
1712+
1713+
// Lockup cliff ends at day 865
1714+
assert_eq!(contract.lockup_information.lockup_duration, to_nanos(YEAR));
1715+
1716+
// Everything is locked and unvested in the beginning
1717+
assert_eq!(
1718+
contract.get_vesting_information(),
1719+
VestingInformation::VestingHash(
1720+
VestingScheduleWithSalt {
1721+
vesting_schedule: vesting_schedule.clone(),
1722+
salt: SALT.to_vec().into(),
1723+
}
1724+
.hash()
1725+
.into()
1726+
)
1727+
);
1728+
assert_eq!(contract.get_owners_balance().0, 0);
1729+
assert_eq!(contract.get_liquid_owners_balance().0, 0);
1730+
assert_eq!(contract.get_locked_amount().0, lockup_amount);
1731+
assert_eq!(
1732+
contract.get_unvested_amount(vesting_schedule.clone()).0,
1733+
lockup_amount
1734+
);
1735+
assert_eq!(
1736+
contract
1737+
.get_locked_vested_amount(vesting_schedule.clone())
1738+
.0,
1739+
0
1740+
);
1741+
1742+
1743+
// *** day 599: day before vesting cliff is passed ***
1744+
let ts_1_day_before_vesting_cliff = to_ts(GENESIS_TIME_IN_DAYS + vesting_cliff_offset - 1);
1745+
assert_eq!(ts_1_day_before_vesting_cliff, vesting_schedule.cliff_timestamp.0 - to_nanos(1));
1746+
context.block_timestamp = ts_1_day_before_vesting_cliff;
1747+
testing_env!(context.clone());
1748+
1749+
// Everything is still locked and unvested
1750+
assert_eq!(contract.get_owners_balance().0, 0);
1751+
assert_eq!(contract.get_liquid_owners_balance().0, 0);
1752+
assert_eq!(contract.get_locked_amount().0, lockup_amount);
1753+
assert_eq!(
1754+
contract.get_unvested_amount(vesting_schedule.clone()).0,
1755+
lockup_amount
1756+
);
1757+
assert_eq!(
1758+
contract
1759+
.get_locked_vested_amount(vesting_schedule.clone())
1760+
.0,
1761+
0
1762+
);
1763+
1764+
1765+
// *** day 600: day of vesting cliff ***
1766+
let ts_day_of_vesting_cliff = to_ts(GENESIS_TIME_IN_DAYS + vesting_cliff_offset);
1767+
assert_eq!(ts_day_of_vesting_cliff, vesting_schedule.cliff_timestamp.0);
1768+
context.block_timestamp = ts_day_of_vesting_cliff;
1769+
testing_env!(context.clone());
1770+
1771+
// 25% is vested
1772+
let vesting_nanos_passed = (ts_day_of_vesting_cliff - ts_vesting_started) as u128;
1773+
let vesting_nanos_total = (vesting_schedule.end_timestamp.0 - vesting_schedule.start_timestamp.0) as u128;
1774+
1775+
let expected_vested_amount_at_cliff_day = lockup_amount / vesting_nanos_total * vesting_nanos_passed;
1776+
let vested_amount_at_cliff_day = contract
1777+
.get_locked_vested_amount(vesting_schedule.clone())
1778+
.0;
1779+
assert_almost_eq_with_max_delta(expected_vested_amount_at_cliff_day, vested_amount_at_cliff_day, to_yocto(1));
1780+
assert_eq!(to_yocto(250000), vested_amount_at_cliff_day);
1781+
1782+
let expected_unvested_amount_at_cliff_day = lockup_amount - expected_vested_amount_at_cliff_day;
1783+
let unvested_amount_at_cliff_day = contract.get_unvested_amount(vesting_schedule.clone()).0;
1784+
assert_almost_eq_with_max_delta(expected_unvested_amount_at_cliff_day, unvested_amount_at_cliff_day, to_yocto(1));
1785+
assert_eq!(to_yocto(750000), unvested_amount_at_cliff_day);
1786+
1787+
// But tokens are still locked due to lockup
1788+
assert_eq!(contract.get_owners_balance().0, 0);
1789+
assert_eq!(contract.get_liquid_owners_balance().0, 0);
1790+
assert_eq!(contract.get_locked_amount().0, lockup_amount);
1791+
1792+
1793+
// *** day 800: day of termination ***
1794+
let ts_termination_day = to_ts(GENESIS_TIME_IN_DAYS + vesting_cliff_offset + 200);
1795+
context.block_timestamp = ts_termination_day;
1796+
testing_env!(context.clone());
1797+
1798+
assert_eq!(
1799+
contract.get_vesting_information(),
1800+
VestingInformation::VestingHash(
1801+
VestingScheduleWithSalt {
1802+
vesting_schedule: vesting_schedule.clone(),
1803+
salt: SALT.to_vec().into(),
1804+
}
1805+
.hash()
1806+
.into()
1807+
)
1808+
);
1809+
1810+
// Some tokens are vested
1811+
let vesting_nanos_passed = (ts_termination_day - ts_vesting_started) as u128;
1812+
let expected_vested_amount_at_termination_day = lockup_amount / vesting_nanos_total * vesting_nanos_passed;
1813+
let locked_vested_amount_at_termination_day = contract
1814+
.get_locked_vested_amount(vesting_schedule.clone())
1815+
.0;
1816+
1817+
assert_almost_eq_with_max_delta(expected_vested_amount_at_termination_day, locked_vested_amount_at_termination_day, to_yocto(1));
1818+
assert_almost_eq_with_max_delta(to_yocto(386986), locked_vested_amount_at_termination_day, to_yocto(1));
1819+
1820+
let expected_unvested_amount_at_termination_day = lockup_amount - expected_vested_amount_at_termination_day;
1821+
let unvested_amount_at_termination_day =
1822+
contract.get_unvested_amount(vesting_schedule.clone()).0;
1823+
assert_almost_eq_with_max_delta(expected_unvested_amount_at_termination_day, unvested_amount_at_termination_day, to_yocto(1));
1824+
assert_almost_eq_with_max_delta(to_yocto(613014), unvested_amount_at_termination_day, to_yocto(1));
1825+
1826+
// But tokens are still locked due to lockup
1827+
assert_eq!(contract.get_owners_balance().0, 0);
1828+
assert_eq!(contract.get_liquid_owners_balance().0, 0);
1829+
assert_eq!(contract.get_locked_amount().0, lockup_amount);
1830+
1831+
// Terminate the vesting
1832+
context.predecessor_account_id = account_foundation();
1833+
context.signer_account_pk = public_key(3).into();
1834+
context.is_view = false;
1835+
testing_env!(context.clone());
1836+
contract.terminate_vesting(Some(VestingScheduleWithSalt {
1837+
vesting_schedule: vesting_schedule.clone(),
1838+
salt: SALT.to_vec().into(),
1839+
}));
1840+
1841+
context.is_view = true;
1842+
testing_env!(context.clone());
1843+
assert_eq!(
1844+
contract.get_vesting_information(),
1845+
VestingInformation::Terminating(TerminationInformation {
1846+
unvested_amount: unvested_amount_at_termination_day.into(),
1847+
status: TerminationStatus::ReadyToWithdraw,
1848+
})
1849+
);
1850+
1851+
1852+
// *** day 801: 1 day after termination ***
1853+
let ts_1_day_after_termination = to_ts(GENESIS_TIME_IN_DAYS + vesting_cliff_offset + 201);
1854+
context.block_timestamp = ts_1_day_after_termination;
1855+
testing_env!(context.clone());
1856+
1857+
// All the tokens are still locked
1858+
assert_eq!(contract.get_owners_balance().0, 0);
1859+
assert_eq!(contract.get_liquid_owners_balance().0, 0);
1860+
assert_eq!(contract.get_locked_amount().0, lockup_amount);
1861+
1862+
// Nothing new is vested since termination
1863+
let unvested_amount_1_day_after_termination =
1864+
contract.get_unvested_amount(vesting_schedule.clone()).0;
1865+
assert_eq!(unvested_amount_1_day_after_termination, unvested_amount_at_termination_day);
1866+
1867+
1868+
// *** day 864: 1 day before lockup cliff is passed ***
1869+
let ts_day_before_lockup_cliff = to_ts(GENESIS_TIME_IN_DAYS + YEAR - 1);
1870+
context.block_timestamp = ts_day_before_lockup_cliff;
1871+
testing_env!(context.clone());
1872+
1873+
// All the tokens are still locked
1874+
assert_eq!(contract.get_owners_balance().0, 0);
1875+
assert_eq!(contract.get_liquid_owners_balance().0, 0);
1876+
assert_eq!(contract.get_locked_amount().0, lockup_amount);
1877+
1878+
// Nothing new is vested since termination
1879+
let unvested_amount_day_before_lockup_cliff =
1880+
contract.get_unvested_amount(vesting_schedule.clone()).0;
1881+
assert_eq!(unvested_amount_day_before_lockup_cliff, unvested_amount_at_termination_day);
1882+
1883+
1884+
// *** day 865: day of lockup cliff ***
1885+
let ts_day_of_lockup_cliff = to_ts(GENESIS_TIME_IN_DAYS + YEAR);
1886+
context.block_timestamp = ts_day_of_lockup_cliff;
1887+
testing_env!(context.clone());
1888+
1889+
let unlocked_amount_day_of_lockup_cliff = contract.get_liquid_owners_balance().0;
1890+
let expected_unlocked_amount_day_of_lockup_cliff = lockup_amount / (contract.lockup_information.release_duration.unwrap() as u128) * (to_nanos(YEAR) as u128);
1891+
assert_almost_eq_with_max_delta(expected_unlocked_amount_day_of_lockup_cliff, unlocked_amount_day_of_lockup_cliff, to_yocto(1));
1892+
1893+
1894+
// *** day 1064: 1 day before unlock stopped ***
1895+
let ts_1_day_before_unlock_stop = to_ts(GENESIS_TIME_IN_DAYS + YEAR + 199);
1896+
context.block_timestamp = ts_1_day_before_unlock_stop;
1897+
testing_env!(context.clone());
1898+
1899+
let locked_amount_1_day_before_unlock_stop = contract.get_locked_amount().0;
1900+
1901+
// *** day 1065, unlock stopped ***
1902+
let ts_day_unlock_stopped = to_ts(GENESIS_TIME_IN_DAYS + YEAR + 200);
1903+
context.block_timestamp = ts_day_unlock_stopped;
1904+
testing_env!(context.clone());
1905+
1906+
let locked_amount_day_unlock_stopped = contract.get_locked_amount().0;
1907+
assert!(
1908+
locked_amount_day_unlock_stopped < locked_amount_1_day_before_unlock_stop,
1909+
"Locked amount should decrease"
1910+
);
1911+
assert_eq!(locked_amount_day_unlock_stopped, unvested_amount_at_termination_day);
1912+
1913+
// We unlock 684 tokens in one day,
1914+
// it means that our delta (1 token) is small enough to detect any issue
1915+
assert_almost_eq_with_max_delta(to_yocto(684), locked_amount_1_day_before_unlock_stop - locked_amount_day_unlock_stopped, to_yocto(1));
1916+
1917+
1918+
// *** day 1066, 1 day after unlock stopped ***
1919+
let ts_1_day_after_unlock_stopped = to_ts(GENESIS_TIME_IN_DAYS + YEAR + 201);
1920+
context.block_timestamp = ts_1_day_after_unlock_stopped;
1921+
testing_env!(context.clone());
1922+
1923+
assert_eq!(contract.get_locked_amount().0, unvested_amount_at_termination_day);
1924+
}
1925+
16651926
#[test]
16661927
fn test_termination_with_staking() {
16671928
let lockup_amount = to_yocto(1000);

0 commit comments

Comments
 (0)