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.
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.
The safety pool approach we take consists out of two distinct mechanisms:
Reference-input-based freezing in order to lock stakes to prevent opting out from universal event.
An admin multisig is able to trigger this without a proposal, but various limitations are encoded in order to prevent it from being abused.
RATs in order to allow managerial slashing of stakes and redistributing it in order to preserve protocol stability.
The flexibility that is provided by the generalization of GATs allows us to encode additional authority checks for effects. These checks make it possible to have a proposal effect perform the steps to slash user tokens.
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.
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 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.