Foundation Callbacks

While you're reading this line, one uses some time to fill in those lacked assertions and functions in foundation.rs, as one mentioned one won't be explaining them (but they still have to be there for it to run). Let's go through quickly what are these functions and where they go into so you can lookup for it yourself from the LockUp Contract.

  • assert_vesting in internal.rs.
  • get_unvested_amount in getters.rs.
  • assert_staking_pool_is_idle in internal.rs.
  • get_termination_status in getters.rs.
  • set_termination_status in internal.rs.
  • set_staking_pool_status in internal.rs.
  • get_terminated_unvested_balance in getters.rs.

So most assertions and setters are in internal.rs and getters in getters.rs (of course).

There's one thing one didn't mentioned before. For the original contract, internal.rs uses pub for their functions, but one thought they're internal, so one changes all of them to pub(crate) just in case the pub is needed, but we don't access it outside the crate.

With these, we're ready to move on to the next part, the foundation_callbacks.rs.

Again, these are still inside the implementation of LockupContract, which we sub-divided them into sections for easier understanding. These are the callback methods for the NEAR Foundation sub-contracts.

The first function gets the account staked balance to unstake that much balance. It's a callback, so we have the #[callback] tag on the argument; which means it calls the env::promise_result(0) given that env::promise_results_count() equals to 1 (hence only single result, and 0 means fetch the 0th element, just like how you fetch elements from an array). Then, the PromiseResult that is returned is assigned to val, whether it being Successful, or Failed will give different value. Note that one hypothesize this might ignored the NotReady trait, since val is a bool and it can only save 2 values, so it makes sense to save the Successful and Failed trait. For more information, again refer to this article.

use crate::*;

use near_sdk::{near_bindgen, PromiseOrValue, assert_self,
  is_promise_success};
use std::convert::Into;


#[near_bindgen]
impl LockupContract {
    /// Called after the request to get the current staking balance to unstake 
    /// everything for vesting schedule termination.
    pub fn on_get_account_staked_balance_to_unstake(
      &mut self,
      #[callback] staked_balance: WrappedBalance,
    ) -> PromiseOrValue<bool> {
      assert_self();

      if staked_balance.0 > 0 {
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{}",
            staked_balance.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

        ext_staking_pool::unstake(
          staked_balance,
          self.staking_information
              .as_ref()
              .unwrap()
              .staking_pool_account_id
              .clone(),
          NO_DEPOSIT,
          gas::staking_pool::UNSTAKE,
        )
        .then(
          ext_self_foundation::on_staking_pool_unstake_for_termination(
            staked_balance,
            env::current_account_id(),
            NO_DEPOSIT,
            gas::foundation_callbacks::ON_STAKING_POOL_UNSTAKE_FOR_TERMINATION,
          ),
        ).into()
      } else {
        env::log_str("Terminating: Nothing to unstake.");
        self.set_staking_pool_status(TransactionStatus::Idle);
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        PromiseOrValue::Value(true)
      }
    }

    /// Called after the given amount is unstaked from the staking pool contract
    /// due to vesting termination. 
    pub fn on_staking_pool_unstake_for_termination(
      &mut self,
      amount: WrappedBalance
    ) -> bool {
      assert_self();

      let unstake_succeeded = is_promise_success();
      self.set_staking_pool_status(TransactionStatus::Idle);

      if unstake_succeeded {
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{} succeeded",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      } else {
        self.set_termination_status(TerminationStatus::VestingTerminatedWithDeficit);
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{} has failed",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      }

      unstake_succeeded
    }

    
    /// Called after the request to get the current unstaked balance to 
    /// withdraw everything from vesting schedule termination. 
    pub fn on_get_account_unstaked_balance_to_withdraw(
      &mut self,
      #[callback] unstaked_balance: WrappedBalance,
    ) -> PromiseOrValue<bool> {
      assert_self();

      if unstaked_balance.0 > 0 {
        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{}",
            unstaked_balance.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

        ext_staking_pool::withdraw(
          unstaked_balance,
          self.staking_information
              .as_ref()
              .unwrap()
              .staking_pool_account_id
              .clone(),
          NO_DEPOSIT,
          gas::staking_pool::WITHDRAW,
        )
        .then(
          ext_self_foundation::on_staking_pool_withdraw_for_termination(
            unstaked_balance,
            env::current_account_id(),
            NO_DEPOSIT,
            gas::foundation_callbacks::ON_STAKING_POOL_WITHDRAW_FOR_TERMINATION,
          ),
        ).into()

      } else {
        env::log_str(
          concat!(
            "Terminating: Nothing to withdraw from staking pool. ", 
            "They had been withdrawn to account, which you can withdraw from to ",
            "your account."
          )
        );
          
        self.set_staking_pool_status(TransactionStatus::Idle);
        self.set_termination_status(TerminationStatus::ReadyToWithdraw);
        PromiseOrValue::Value(true)
      }
    }

    /// Called after the given amount is unstaked from the staking pool contract
    /// due to vesting termination. 
    pub fn on_staking_pool_withdraw_for_termination(
      &mut self, 
      amount: WrappedBalance
    ) -> bool {
      assert_self();

      let withdraw_succeeded = is_promise_success();
      self.set_staking_pool_status(TransactionStatus::Idle);

      if withdraw_succeeded {
        self.set_termination_status(TerminationStatus::ReadyToWithdraw); 

        {
          let staking_information = self.staking_information.as_mut().unwrap();

          // Due to staking rewards, deposit can be negative. 
          staking_information.deposit_amount.0 = staking_information
              .deposit_amount.0.saturating_sub(amount.0);
        }

        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{} succeeded.",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

      } else {
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{} failed.",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      }

      withdraw_succeeded
    }

    /// Called after the foundation tried to withdraw the terminated unvested
    /// balance.
    pub fn on_withdraw_unvested_amount(
      &mut self,
      amount: WrappedBalance,
      receiver_id: AccountId
    ) -> bool {
      assert_self();

      let withdraw_succeeded = is_promise_success();

      if withdraw_succeeded {
        env::log_str(
          format!(
            "Terminating: Withdrawing {} to @{} succeeded.",
            amount.0, receiver_id
          ).as_str(),
        );

        // Decreasing lockup amount after withdrawal.
        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.vesting_information = 
              VestingInformation::Terminating(TerminationInformation {
                unvested_amount: remaining_balance.into(),
                status: TerminationStatus::ReadyToWithdraw,
              });

          env::log_str(
            format!(
              "Terminating: {} still awaits withdrawal",
              remaining_balance
            ).as_str(),
          );

          if self.get_account_balance().0 == 0 {
            env::log_str(
              concat!("The withdrawal is completed: no more balance can be ",
              "withdrawn in a future call.")
            );
          }

        } else {
          self.foundation_account_id = None;
          self.vesting_information = VestingInformation::None;
          env::log_str("Vesting schedule termination and withdrawal completed.");
        }

      } else {
        self.set_termination_status(TerminationStatus::ReadyToWithdraw);
        env::log_str(
          format!(
            "Terminating: Withdrawing {} to @{} FAILED.",
            amount.0, receiver_id,
          ).as_str(),
        );
      }

      withdraw_succeeded
    }
}

If staked_balance is positive, we have something we can unstake, otherwise we'll just say nothing to unstake and move on. If there is something to unstake, we'll perform cross-contract calls with the staking pool contract, asking them to unstake whatever we have, then we'll perform (a series of) callbacks to clean up the calls. To emphasize, callbacks can be considered the last function call of a series of function calls to clear up everything that needs to be clean up, and finalizes the function call. This is performed via a Promise, which is the contract calling on itself (a cross-contract call that itself call on itself) for cleaning up.

Here, we have this itself as a callback, calling another callback in the .then(), which is a function called on_staking_pool_unstake_for_termination. We'll look at this next (since this function is in the same Rust file as the function you had just seen).

use crate::*;

use near_sdk::{near_bindgen, PromiseOrValue, assert_self,
  is_promise_success};
use std::convert::Into;


#[near_bindgen]
impl LockupContract {
    /// Called after the request to get the current staking balance to unstake 
    /// everything for vesting schedule termination.
    pub fn on_get_account_staked_balance_to_unstake(
      &mut self,
      #[callback] staked_balance: WrappedBalance,
    ) -> PromiseOrValue<bool> {
      assert_self();

      if staked_balance.0 > 0 {
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{}",
            staked_balance.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

        ext_staking_pool::unstake(
          staked_balance,
          self.staking_information
              .as_ref()
              .unwrap()
              .staking_pool_account_id
              .clone(),
          NO_DEPOSIT,
          gas::staking_pool::UNSTAKE,
        )
        .then(
          ext_self_foundation::on_staking_pool_unstake_for_termination(
            staked_balance,
            env::current_account_id(),
            NO_DEPOSIT,
            gas::foundation_callbacks::ON_STAKING_POOL_UNSTAKE_FOR_TERMINATION,
          ),
        ).into()
      } else {
        env::log_str("Terminating: Nothing to unstake.");
        self.set_staking_pool_status(TransactionStatus::Idle);
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        PromiseOrValue::Value(true)
      }
    }

    /// Called after the given amount is unstaked from the staking pool contract
    /// due to vesting termination. 
    pub fn on_staking_pool_unstake_for_termination(
      &mut self,
      amount: WrappedBalance
    ) -> bool {
      assert_self();

      let unstake_succeeded = is_promise_success();
      self.set_staking_pool_status(TransactionStatus::Idle);

      if unstake_succeeded {
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{} succeeded",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      } else {
        self.set_termination_status(TerminationStatus::VestingTerminatedWithDeficit);
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{} has failed",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      }

      unstake_succeeded
    }

    
    /// Called after the request to get the current unstaked balance to 
    /// withdraw everything from vesting schedule termination. 
    pub fn on_get_account_unstaked_balance_to_withdraw(
      &mut self,
      #[callback] unstaked_balance: WrappedBalance,
    ) -> PromiseOrValue<bool> {
      assert_self();

      if unstaked_balance.0 > 0 {
        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{}",
            unstaked_balance.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

        ext_staking_pool::withdraw(
          unstaked_balance,
          self.staking_information
              .as_ref()
              .unwrap()
              .staking_pool_account_id
              .clone(),
          NO_DEPOSIT,
          gas::staking_pool::WITHDRAW,
        )
        .then(
          ext_self_foundation::on_staking_pool_withdraw_for_termination(
            unstaked_balance,
            env::current_account_id(),
            NO_DEPOSIT,
            gas::foundation_callbacks::ON_STAKING_POOL_WITHDRAW_FOR_TERMINATION,
          ),
        ).into()

      } else {
        env::log_str(
          concat!(
            "Terminating: Nothing to withdraw from staking pool. ", 
            "They had been withdrawn to account, which you can withdraw from to ",
            "your account."
          )
        );
          
        self.set_staking_pool_status(TransactionStatus::Idle);
        self.set_termination_status(TerminationStatus::ReadyToWithdraw);
        PromiseOrValue::Value(true)
      }
    }

    /// Called after the given amount is unstaked from the staking pool contract
    /// due to vesting termination. 
    pub fn on_staking_pool_withdraw_for_termination(
      &mut self, 
      amount: WrappedBalance
    ) -> bool {
      assert_self();

      let withdraw_succeeded = is_promise_success();
      self.set_staking_pool_status(TransactionStatus::Idle);

      if withdraw_succeeded {
        self.set_termination_status(TerminationStatus::ReadyToWithdraw); 

        {
          let staking_information = self.staking_information.as_mut().unwrap();

          // Due to staking rewards, deposit can be negative. 
          staking_information.deposit_amount.0 = staking_information
              .deposit_amount.0.saturating_sub(amount.0);
        }

        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{} succeeded.",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

      } else {
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{} failed.",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      }

      withdraw_succeeded
    }

    /// Called after the foundation tried to withdraw the terminated unvested
    /// balance.
    pub fn on_withdraw_unvested_amount(
      &mut self,
      amount: WrappedBalance,
      receiver_id: AccountId
    ) -> bool {
      assert_self();

      let withdraw_succeeded = is_promise_success();

      if withdraw_succeeded {
        env::log_str(
          format!(
            "Terminating: Withdrawing {} to @{} succeeded.",
            amount.0, receiver_id
          ).as_str(),
        );

        // Decreasing lockup amount after withdrawal.
        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.vesting_information = 
              VestingInformation::Terminating(TerminationInformation {
                unvested_amount: remaining_balance.into(),
                status: TerminationStatus::ReadyToWithdraw,
              });

          env::log_str(
            format!(
              "Terminating: {} still awaits withdrawal",
              remaining_balance
            ).as_str(),
          );

          if self.get_account_balance().0 == 0 {
            env::log_str(
              concat!("The withdrawal is completed: no more balance can be ",
              "withdrawn in a future call.")
            );
          }

        } else {
          self.foundation_account_id = None;
          self.vesting_information = VestingInformation::None;
          env::log_str("Vesting schedule termination and withdrawal completed.");
        }

      } else {
        self.set_termination_status(TerminationStatus::ReadyToWithdraw);
        env::log_str(
          format!(
            "Terminating: Withdrawing {} to @{} FAILED.",
            amount.0, receiver_id,
          ).as_str(),
        );
      }

      withdraw_succeeded
    }
}

The first thing you might be wondering, why are we repeating the part in env::log_str except for the log message; why can't we have a function that stores these information and when we call it with tuple unpacking to unpack the information than repeating the code twice?

Well, you can of course. The original code is written separately because they don't want to use variables, as variables means extra computation and storage; and on-chain, that means extra gas fee. So if you could confirm by minimizing the code repetition it doesn't affect the gas fee (even in the slightest bit), go ahead with it. If it does affect gas fee, then you need to think of tradeoffs. If you tradeoff 100 functions, these gas fee adds up. Do you have enough gas to perform the function, and are you willing to pay for the function call if you're going to call it a billion times in the future, adding the fee up? And from a programmers' perspective, is your code easy to read (also important though not related to previous questions)?

Now, we're going to move on to the second part. The previous two functions are responsible for unstaking the staked balance; while the next two functions are responsible for withdrawing the unstaked balance. Just like the first function calling the second function as a promise; the next two function will have its first function (the third function overall) calling the second function (the fourth function overall) with a Promise. The logic flow are about similar, just trying to do different work.

use crate::*;

use near_sdk::{near_bindgen, PromiseOrValue, assert_self,
  is_promise_success};
use std::convert::Into;


#[near_bindgen]
impl LockupContract {
    /// Called after the request to get the current staking balance to unstake 
    /// everything for vesting schedule termination.
    pub fn on_get_account_staked_balance_to_unstake(
      &mut self,
      #[callback] staked_balance: WrappedBalance,
    ) -> PromiseOrValue<bool> {
      assert_self();

      if staked_balance.0 > 0 {
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{}",
            staked_balance.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

        ext_staking_pool::unstake(
          staked_balance,
          self.staking_information
              .as_ref()
              .unwrap()
              .staking_pool_account_id
              .clone(),
          NO_DEPOSIT,
          gas::staking_pool::UNSTAKE,
        )
        .then(
          ext_self_foundation::on_staking_pool_unstake_for_termination(
            staked_balance,
            env::current_account_id(),
            NO_DEPOSIT,
            gas::foundation_callbacks::ON_STAKING_POOL_UNSTAKE_FOR_TERMINATION,
          ),
        ).into()
      } else {
        env::log_str("Terminating: Nothing to unstake.");
        self.set_staking_pool_status(TransactionStatus::Idle);
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        PromiseOrValue::Value(true)
      }
    }

    /// Called after the given amount is unstaked from the staking pool contract
    /// due to vesting termination. 
    pub fn on_staking_pool_unstake_for_termination(
      &mut self,
      amount: WrappedBalance
    ) -> bool {
      assert_self();

      let unstake_succeeded = is_promise_success();
      self.set_staking_pool_status(TransactionStatus::Idle);

      if unstake_succeeded {
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{} succeeded",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      } else {
        self.set_termination_status(TerminationStatus::VestingTerminatedWithDeficit);
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{} has failed",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      }

      unstake_succeeded
    }

    
    /// Called after the request to get the current unstaked balance to 
    /// withdraw everything from vesting schedule termination. 
    pub fn on_get_account_unstaked_balance_to_withdraw(
      &mut self,
      #[callback] unstaked_balance: WrappedBalance,
    ) -> PromiseOrValue<bool> {
      assert_self();

      if unstaked_balance.0 > 0 {
        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{}",
            unstaked_balance.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

        ext_staking_pool::withdraw(
          unstaked_balance,
          self.staking_information
              .as_ref()
              .unwrap()
              .staking_pool_account_id
              .clone(),
          NO_DEPOSIT,
          gas::staking_pool::WITHDRAW,
        )
        .then(
          ext_self_foundation::on_staking_pool_withdraw_for_termination(
            unstaked_balance,
            env::current_account_id(),
            NO_DEPOSIT,
            gas::foundation_callbacks::ON_STAKING_POOL_WITHDRAW_FOR_TERMINATION,
          ),
        ).into()

      } else {
        env::log_str(
          concat!(
            "Terminating: Nothing to withdraw from staking pool. ", 
            "They had been withdrawn to account, which you can withdraw from to ",
            "your account."
          )
        );
          
        self.set_staking_pool_status(TransactionStatus::Idle);
        self.set_termination_status(TerminationStatus::ReadyToWithdraw);
        PromiseOrValue::Value(true)
      }
    }

    /// Called after the given amount is unstaked from the staking pool contract
    /// due to vesting termination. 
    pub fn on_staking_pool_withdraw_for_termination(
      &mut self, 
      amount: WrappedBalance
    ) -> bool {
      assert_self();

      let withdraw_succeeded = is_promise_success();
      self.set_staking_pool_status(TransactionStatus::Idle);

      if withdraw_succeeded {
        self.set_termination_status(TerminationStatus::ReadyToWithdraw); 

        {
          let staking_information = self.staking_information.as_mut().unwrap();

          // Due to staking rewards, deposit can be negative. 
          staking_information.deposit_amount.0 = staking_information
              .deposit_amount.0.saturating_sub(amount.0);
        }

        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{} succeeded.",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

      } else {
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{} failed.",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      }

      withdraw_succeeded
    }

    /// Called after the foundation tried to withdraw the terminated unvested
    /// balance.
    pub fn on_withdraw_unvested_amount(
      &mut self,
      amount: WrappedBalance,
      receiver_id: AccountId
    ) -> bool {
      assert_self();

      let withdraw_succeeded = is_promise_success();

      if withdraw_succeeded {
        env::log_str(
          format!(
            "Terminating: Withdrawing {} to @{} succeeded.",
            amount.0, receiver_id
          ).as_str(),
        );

        // Decreasing lockup amount after withdrawal.
        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.vesting_information = 
              VestingInformation::Terminating(TerminationInformation {
                unvested_amount: remaining_balance.into(),
                status: TerminationStatus::ReadyToWithdraw,
              });

          env::log_str(
            format!(
              "Terminating: {} still awaits withdrawal",
              remaining_balance
            ).as_str(),
          );

          if self.get_account_balance().0 == 0 {
            env::log_str(
              concat!("The withdrawal is completed: no more balance can be ",
              "withdrawn in a future call.")
            );
          }

        } else {
          self.foundation_account_id = None;
          self.vesting_information = VestingInformation::None;
          env::log_str("Vesting schedule termination and withdrawal completed.");
        }

      } else {
        self.set_termination_status(TerminationStatus::ReadyToWithdraw);
        env::log_str(
          format!(
            "Terminating: Withdrawing {} to @{} FAILED.",
            amount.0, receiver_id,
          ).as_str(),
        );
      }

      withdraw_succeeded
    }
}
use crate::*;

use near_sdk::{near_bindgen, PromiseOrValue, assert_self,
  is_promise_success};
use std::convert::Into;


#[near_bindgen]
impl LockupContract {
    /// Called after the request to get the current staking balance to unstake 
    /// everything for vesting schedule termination.
    pub fn on_get_account_staked_balance_to_unstake(
      &mut self,
      #[callback] staked_balance: WrappedBalance,
    ) -> PromiseOrValue<bool> {
      assert_self();

      if staked_balance.0 > 0 {
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{}",
            staked_balance.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

        ext_staking_pool::unstake(
          staked_balance,
          self.staking_information
              .as_ref()
              .unwrap()
              .staking_pool_account_id
              .clone(),
          NO_DEPOSIT,
          gas::staking_pool::UNSTAKE,
        )
        .then(
          ext_self_foundation::on_staking_pool_unstake_for_termination(
            staked_balance,
            env::current_account_id(),
            NO_DEPOSIT,
            gas::foundation_callbacks::ON_STAKING_POOL_UNSTAKE_FOR_TERMINATION,
          ),
        ).into()
      } else {
        env::log_str("Terminating: Nothing to unstake.");
        self.set_staking_pool_status(TransactionStatus::Idle);
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        PromiseOrValue::Value(true)
      }
    }

    /// Called after the given amount is unstaked from the staking pool contract
    /// due to vesting termination. 
    pub fn on_staking_pool_unstake_for_termination(
      &mut self,
      amount: WrappedBalance
    ) -> bool {
      assert_self();

      let unstake_succeeded = is_promise_success();
      self.set_staking_pool_status(TransactionStatus::Idle);

      if unstake_succeeded {
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{} succeeded",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      } else {
        self.set_termination_status(TerminationStatus::VestingTerminatedWithDeficit);
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{} has failed",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      }

      unstake_succeeded
    }

    
    /// Called after the request to get the current unstaked balance to 
    /// withdraw everything from vesting schedule termination. 
    pub fn on_get_account_unstaked_balance_to_withdraw(
      &mut self,
      #[callback] unstaked_balance: WrappedBalance,
    ) -> PromiseOrValue<bool> {
      assert_self();

      if unstaked_balance.0 > 0 {
        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{}",
            unstaked_balance.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

        ext_staking_pool::withdraw(
          unstaked_balance,
          self.staking_information
              .as_ref()
              .unwrap()
              .staking_pool_account_id
              .clone(),
          NO_DEPOSIT,
          gas::staking_pool::WITHDRAW,
        )
        .then(
          ext_self_foundation::on_staking_pool_withdraw_for_termination(
            unstaked_balance,
            env::current_account_id(),
            NO_DEPOSIT,
            gas::foundation_callbacks::ON_STAKING_POOL_WITHDRAW_FOR_TERMINATION,
          ),
        ).into()

      } else {
        env::log_str(
          concat!(
            "Terminating: Nothing to withdraw from staking pool. ", 
            "They had been withdrawn to account, which you can withdraw from to ",
            "your account."
          )
        );
          
        self.set_staking_pool_status(TransactionStatus::Idle);
        self.set_termination_status(TerminationStatus::ReadyToWithdraw);
        PromiseOrValue::Value(true)
      }
    }

    /// Called after the given amount is unstaked from the staking pool contract
    /// due to vesting termination. 
    pub fn on_staking_pool_withdraw_for_termination(
      &mut self, 
      amount: WrappedBalance
    ) -> bool {
      assert_self();

      let withdraw_succeeded = is_promise_success();
      self.set_staking_pool_status(TransactionStatus::Idle);

      if withdraw_succeeded {
        self.set_termination_status(TerminationStatus::ReadyToWithdraw); 

        {
          let staking_information = self.staking_information.as_mut().unwrap();

          // Due to staking rewards, deposit can be negative. 
          staking_information.deposit_amount.0 = staking_information
              .deposit_amount.0.saturating_sub(amount.0);
        }

        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{} succeeded.",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

      } else {
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{} failed.",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      }

      withdraw_succeeded
    }

    /// Called after the foundation tried to withdraw the terminated unvested
    /// balance.
    pub fn on_withdraw_unvested_amount(
      &mut self,
      amount: WrappedBalance,
      receiver_id: AccountId
    ) -> bool {
      assert_self();

      let withdraw_succeeded = is_promise_success();

      if withdraw_succeeded {
        env::log_str(
          format!(
            "Terminating: Withdrawing {} to @{} succeeded.",
            amount.0, receiver_id
          ).as_str(),
        );

        // Decreasing lockup amount after withdrawal.
        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.vesting_information = 
              VestingInformation::Terminating(TerminationInformation {
                unvested_amount: remaining_balance.into(),
                status: TerminationStatus::ReadyToWithdraw,
              });

          env::log_str(
            format!(
              "Terminating: {} still awaits withdrawal",
              remaining_balance
            ).as_str(),
          );

          if self.get_account_balance().0 == 0 {
            env::log_str(
              concat!("The withdrawal is completed: no more balance can be ",
              "withdrawn in a future call.")
            );
          }

        } else {
          self.foundation_account_id = None;
          self.vesting_information = VestingInformation::None;
          env::log_str("Vesting schedule termination and withdrawal completed.");
        }

      } else {
        self.set_termination_status(TerminationStatus::ReadyToWithdraw);
        env::log_str(
          format!(
            "Terminating: Withdrawing {} to @{} FAILED.",
            amount.0, receiver_id,
          ).as_str(),
        );
      }

      withdraw_succeeded
    }
}

And I have no idea why there is a curly braces around the if withdraw_succeeded block.

Lastly, we have the callbacks for termination_withdraw function. This callback will be called when withdrawing unvested amount.

use crate::*;

use near_sdk::{near_bindgen, PromiseOrValue, assert_self,
  is_promise_success};
use std::convert::Into;


#[near_bindgen]
impl LockupContract {
    /// Called after the request to get the current staking balance to unstake 
    /// everything for vesting schedule termination.
    pub fn on_get_account_staked_balance_to_unstake(
      &mut self,
      #[callback] staked_balance: WrappedBalance,
    ) -> PromiseOrValue<bool> {
      assert_self();

      if staked_balance.0 > 0 {
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{}",
            staked_balance.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

        ext_staking_pool::unstake(
          staked_balance,
          self.staking_information
              .as_ref()
              .unwrap()
              .staking_pool_account_id
              .clone(),
          NO_DEPOSIT,
          gas::staking_pool::UNSTAKE,
        )
        .then(
          ext_self_foundation::on_staking_pool_unstake_for_termination(
            staked_balance,
            env::current_account_id(),
            NO_DEPOSIT,
            gas::foundation_callbacks::ON_STAKING_POOL_UNSTAKE_FOR_TERMINATION,
          ),
        ).into()
      } else {
        env::log_str("Terminating: Nothing to unstake.");
        self.set_staking_pool_status(TransactionStatus::Idle);
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        PromiseOrValue::Value(true)
      }
    }

    /// Called after the given amount is unstaked from the staking pool contract
    /// due to vesting termination. 
    pub fn on_staking_pool_unstake_for_termination(
      &mut self,
      amount: WrappedBalance
    ) -> bool {
      assert_self();

      let unstake_succeeded = is_promise_success();
      self.set_staking_pool_status(TransactionStatus::Idle);

      if unstake_succeeded {
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{} succeeded",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      } else {
        self.set_termination_status(TerminationStatus::VestingTerminatedWithDeficit);
        env::log_str(
          format!(
            "Terminating: Unstaking {} from @{} has failed",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      }

      unstake_succeeded
    }

    
    /// Called after the request to get the current unstaked balance to 
    /// withdraw everything from vesting schedule termination. 
    pub fn on_get_account_unstaked_balance_to_withdraw(
      &mut self,
      #[callback] unstaked_balance: WrappedBalance,
    ) -> PromiseOrValue<bool> {
      assert_self();

      if unstaked_balance.0 > 0 {
        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{}",
            unstaked_balance.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

        ext_staking_pool::withdraw(
          unstaked_balance,
          self.staking_information
              .as_ref()
              .unwrap()
              .staking_pool_account_id
              .clone(),
          NO_DEPOSIT,
          gas::staking_pool::WITHDRAW,
        )
        .then(
          ext_self_foundation::on_staking_pool_withdraw_for_termination(
            unstaked_balance,
            env::current_account_id(),
            NO_DEPOSIT,
            gas::foundation_callbacks::ON_STAKING_POOL_WITHDRAW_FOR_TERMINATION,
          ),
        ).into()

      } else {
        env::log_str(
          concat!(
            "Terminating: Nothing to withdraw from staking pool. ", 
            "They had been withdrawn to account, which you can withdraw from to ",
            "your account."
          )
        );
          
        self.set_staking_pool_status(TransactionStatus::Idle);
        self.set_termination_status(TerminationStatus::ReadyToWithdraw);
        PromiseOrValue::Value(true)
      }
    }

    /// Called after the given amount is unstaked from the staking pool contract
    /// due to vesting termination. 
    pub fn on_staking_pool_withdraw_for_termination(
      &mut self, 
      amount: WrappedBalance
    ) -> bool {
      assert_self();

      let withdraw_succeeded = is_promise_success();
      self.set_staking_pool_status(TransactionStatus::Idle);

      if withdraw_succeeded {
        self.set_termination_status(TerminationStatus::ReadyToWithdraw); 

        {
          let staking_information = self.staking_information.as_mut().unwrap();

          // Due to staking rewards, deposit can be negative. 
          staking_information.deposit_amount.0 = staking_information
              .deposit_amount.0.saturating_sub(amount.0);
        }

        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{} succeeded.",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );

      } else {
        self.set_termination_status(TerminationStatus::EverythingUnstaked);
        env::log_str(
          format!(
            "Terminating: Withdrawing {} from @{} failed.",
            amount.0,
            self.staking_information
                .as_ref()
                .unwrap()
                .staking_pool_account_id
                .clone()
          ).as_str(),
        );
      }

      withdraw_succeeded
    }

    /// Called after the foundation tried to withdraw the terminated unvested
    /// balance.
    pub fn on_withdraw_unvested_amount(
      &mut self,
      amount: WrappedBalance,
      receiver_id: AccountId
    ) -> bool {
      assert_self();

      let withdraw_succeeded = is_promise_success();

      if withdraw_succeeded {
        env::log_str(
          format!(
            "Terminating: Withdrawing {} to @{} succeeded.",
            amount.0, receiver_id
          ).as_str(),
        );

        // Decreasing lockup amount after withdrawal.
        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.vesting_information = 
              VestingInformation::Terminating(TerminationInformation {
                unvested_amount: remaining_balance.into(),
                status: TerminationStatus::ReadyToWithdraw,
              });

          env::log_str(
            format!(
              "Terminating: {} still awaits withdrawal",
              remaining_balance
            ).as_str(),
          );

          if self.get_account_balance().0 == 0 {
            env::log_str(
              concat!("The withdrawal is completed: no more balance can be ",
              "withdrawn in a future call.")
            );
          }

        } else {
          self.foundation_account_id = None;
          self.vesting_information = VestingInformation::None;
          env::log_str("Vesting schedule termination and withdrawal completed.");
        }

      } else {
        self.set_termination_status(TerminationStatus::ReadyToWithdraw);
        env::log_str(
          format!(
            "Terminating: Withdrawing {} to @{} FAILED.",
            amount.0, receiver_id,
          ).as_str(),
        );
      }

      withdraw_succeeded
    }
}

Since these foundation callbacks are callbacks, we won't be testing them independently. Rather, our test would start with foundation.rs, which we have done before but cannot continue because the contract is not finished (requirement of owner.rs, which we would be looking at next).