In order for Agora’s staking pool to act as a safety pool, it needs to be able to support a workflow for slashing staked governance tokens (GT) to act as a safety mechanism. (Note: we use "slashing" to mean taking away some token from a particular user.) This document outlines the changes that need to be made to Agora in order to support this.

Motivation

In the event of a protocol suffering loss of funds through a shortfall event, slashing a percentage of locked GT can be used to attempt a recovery. Ultimately, doing this is beneficial for the stakeholders because it allows the protocol to recover and eventually benefits them as well (even though they bear the initial cost). Striking a balance (in the form of the right percentage slashed) is important in order for stakeholders to want to vote in favour of a proposal that results in such a slashing.

Overview

The safety pool approach we take consists out of two distinct mechanisms:

These two mechanisms are implemented essentially completely in isolation, but they work together to allow the safety pool mechanism to function properly. Let’s explore each of them in greater detail.

Reference-input-based freezing of stakes

PlutusV2 brings reference inputs which allow us to witness datums without spending them. Using this mechanism, we will implement admin-controlled locking of stakes such that we are able to run a slashing proposal faithfully.

Implementing this is quite simple. We need a single script which contains a datum that encodes whether or not the stakes should be locked, and if they are frozen, then when it got frozen.

data StakeFreezeDatum
  = StakeFreezeDatum
  { frozen :: Maybe POSIXTime
    -- ^ Have the stakes been frozen? If so, at what time did they get frozen?
  , freezeTime :: POSIXTime
    -- ^ How long does freezing last?
  , cooldownPeriod :: POSIXTime
    -- ^ How long until freezing can happen again
  }

The redeemer is also quite simple

data StakeFreezeRedeemer
  = -- | The multisig can call this at any point, provided we are not in a cooldown period or already frozen. Keep in mind, the 'frozen' field may already have been set, but this will reset it.
    Freeze

Now, with this script in place, we can simply require that all freezable scripts must check in a reference input that this isn’t locked.

This can be done by inspecting the referenceInputs field:

$$ \forall r \in \text{refInputs}\ .\ r.address = stakeScriptAddress \land \neg r.datum.frozen $$

This check is as simple as it gets, which is exactly what is so nice about reference inputs, we get to encode useful behavior without the burden of contention getting in our way.

The slashing effect

The admin multisig wallet can now, at any given moment, lock all the stakes for a short duration. This means we can now create a proposal, whose effects can operate on all stakes, and nobody can opt-out of.