StableMaster - Stablecoin Accounting
Maintaining the integrity of a stablecoin

1. Introduction

StableMaster is the contract handling all the collateral types accepted for a given stablecoin: there is one such contract per stablecoin of the protocol.
It does all the accounting and contains the logic for the fees that are taken to users minting and burning, and for the exchange rate between sanTokens and collateral. This contract is the point of entry in the protocol for stable holders and seekers as well as SLPs.
Besides letting users mint and burn stablecoins as well as SLPs deposit or withdraw collateral, this contract stores for each collateral some accounting quantities like the stocksUsers that is the amount of stablecoins that were minted/burnt using this collateral type, or the sanRate that is the exchange rate between sanTokens and collateral.
This contract helps to propagate governance changes across all the contracts of the protocol that have to do with this stablecoin (all the PoolManager for instance). It is also at the level of this contract that governance can deploy new collateral types or set new parameters.
Although this contract involves collateral transfer transactions, collateral, sanTokens or agTokens is never stored on it. When someone sends collateral to the StableMaster to get stablecoins, this collateral directly goes to the PoolManager of the associated collateral.

2. Contract Details


Implements FunctionUtils, AccessControlUpgradeable, PausableMapUpgradeable, IStableMaster.
The contract is upgradeable.



  • agToken: Reference to the agToken used in this StableMaster
  • _managerList: List of all the collateral managers, that is the PoolManager contracts handling the different types of accepted collateral
  • _core : Reference to the Core contract of the protocol

Collateral Governance References and Variables

Each accepted collateral has its own set of parameters that are specified in a mapping collateralMap between the corresponding PoolManager contract to a struct of references, variables and parameters. References and variables associated to a collateral include:
  • token: Interface for the token accepted by the underlying PoolManager contract
  • sanToken: Reference to the SanToken for the pool
  • perpetualManager: Reference to the PerpetualManagerfor the pool
  • oracle: Address of the oracle for the change rate between collateral and the corresponding stablecoin
  • stocksUsers: Amount of stablecoins minted using this collateral minus the amount of stablecoins that was burnt while asking for this collateral type
  • sanRate: Exchange rate between sanTokens and collateral
  • collatBase: Base used in the collateral implementation
  • slpData: Struct containing parameters for SLPs in the protocol
  • feeData: Struct containing the fee parameters of the protocol
StableMaster contracts recognize a collateral from the address of the PoolManager associated associated to this collateral.

SLPs Parameters and Variables For a Given Collateral

  • lastBlockUpdated: Last timestamp at which the sanRate has been updated for SLPs.
  • lockedInterests : Fees accumulated from previous blocks and to be distributed to SLPs
  • maxInterestsDistributed: Max interests used to update the sanRate in a single block. It should be in collateral token base
The three parameters above are used to prevent front-running/flash-loan attacks for SLPs and to make sure that the sanRate does not increase by too much in a small period. Someone seeing that lots of interests are to be distributed to SLPs could for instance take a flash loan, become a SLP, receive the interests and then withdraw its collateral. By not distributing all the fees directly to SLPs and locking some interests, Angle protocol manages to avoid such manipulations.
  • slippage: Slippage factor that's applied to SLPs exiting (depends on collateral ratio). If it is equal to BASE, SLPs can get nothing, if this is null, SLPs can get their full claim. The parameter is updated by keepers
  • slippageFee: Part of the fees normally going to SLPs that is left aside before the protocol is collateralized back again (depends on collateral ratio). Updated by keepers
  • feesAside: Amount of fees left aside for SLPs and that will be distributed when the protocol is collateralized back again
  • feesForSLPs: Portion of the fees from users minting and burning that goes to SLPs
  • interestsForSLPs: Portion of the interests from lending going to SLPs (the rest goes to surplus)

Collateral Governance Fee Parameters and Variables

Like everywhere in the protocol, all the fees are scaled by BASE_PARAMS:
  • xFeeMint, xFeeBurn: Values of the thresholds to compute the minting or burning fees depending on HA coverage
  • yFeeMint, yFeeBurn: Values of the fees at thresholds
  • bonusMalusMint, bonusMalusBurn: Minting/Burning fees correction set by the FeeManager contract depending on the collateral ratio: they are going to be multiplied to the value of the fees computed using the hedge curve. These parameters can either serve as a bonus or as a malus. If the bonusMalus > BASE_PARAMS , then agents incur a malus and will have larger fees, while fee < BASE_PARAMS they incur a smaller fee than what they would have if fees just consisted in what was obtained using coverage.
  • capOnStableMinted: Parameter used to limit the number of stablecoins that can be issued using the concerned collateral

Access Control

Only the CORE_ROLE, the GOVERNOR_ROLE and the GUARDIAN_ROLE are defined in this contract with the Core being the admin of all roles.
As the StableMaster needs to be called by FeeManagerand PerpetualManager , it needs a way to recognize these contracts. The StableMaster uses a _contractMap that is a mapping between an address and the PoolManager contract associated. Using this is cheaper than OpenZeppelin Access Control logic.

3. Key Mechanisms & Concepts

User Entry/Exit Functions

The following functions are the main points of entry for users to mint and burn stablecoins of the protocol. In each case, either when stablecoins or collateral are involved, users get the possibility to select the beneficiary address of the transaction.
The following functions all have a poolManager parameter which specifies the collateral brought or given in return (in the burn case) . It is hence possible to come and mint agEUR for DAI and redeem USDC from these agEUR.


function mint(uint256 amount, address user, IPoolManager poolManager, uint256 minStableAmount) external;
Lets a user add collateral to the system to mint stablecoins. While the collateral involved in the transaction and in a quantity specified by amount will always be taken from the msg.sender, it is possible to have this collateral sent to another address.
To check that the PoolManager and hence the collateral brought by the user valid, the contract verifies that the collateral associated to it has well a token associated to it in the collateralMap mapping.
Note that it is impossible to mint tokens and to have them sent to the zero address.
  • amount: Amount of collateral sent
  • user: Address of the contract or the person to give the minted tokens to
  • poolManager: Address of the PoolManager of the required collateral
  • minStableAmount: Minimum amount of stablecoins the user wants to get with this transaction. This serves as a slippage protection for users.


function burn(uint256 amount, address burner, address dest, IPoolManager poolManager, uint256 minCollatAmount) external;
Updates variables to take the burn of agTokens (stablecoins) into account, computes transaction fees and gives collateral from the PoolManager in exchange for that.
With this function, it is therefore possible to burn stablecoins on behalf of an address (burner) and then send the obtained collateral to another dest address. The msg.sender should however have approval to burn from the burner or the msg.sender should be the burner.
  • amount: Amount of stable asset burnt
  • burner: Address from which the agTokens will be burnt
  • dest: Address where collateral is going to be
  • poolManager: Collateral type requested by the user burning
  • minCollatAmount: Minimum amount of collateral requested by the user burning stablecoins. It is a slippage protection for users.

SLP Entry/Exit Functions


function deposit(uint256 amount, address user, IPoolManager poolManager) external;
Lets a SLP enter the protocol by adding collateral to the system in exchange of sanTokens
  • user: Address of the SLP to send sanTokens to
  • amount: Amount of collateral sent
  • poolManager: Address of the PoolManager of the required collateral


function withdraw(uint256 amount, address burner, address dest, IPoolManager poolManager) external;
Updates variables to account for the burn of sanTokens by a SLP and gives the corresponding collateral back in exchange.
The msg.sender should have approval to burn from the burner or the msg.sender should be the burner.
  • amount: Amount of sanTokens burnt by the SLP
  • burner: Address that will burn its sanTokens
  • dest: Address that will receive the collateral
  • poolManager: Address of the PoolManager of the required collateral

External View Functions


function getCollateralRatio() external view returns (uint256);
Returns the collateral ratio for this stablecoin, that is the ratio between the collateral that is controlled by the protocol expressed in stablecoin value and the amount of stablecoins issued.
The following functions are functions that can only be called by other contracts of the protocol. In some cases, the associated collateral is not specified (there is no poolManager parameter in the functions). The reason is that the StableMaster can automatically recognize the caller contract through a _contractMap.


function updateStocksUsers(uint256 amount, address poolManager) external;
This function can only be called by the agToken contract. It allows the agToken contract to update the stocksUsers for a given collateral after a burn with no redeem.
  • amount: Amount by which stocksUsers should decrease
  • poolManager: Reference to PoolManager for which stocksUsers needs to be updated


function accumulateInterest(uint256 gain) external;
Takes into account the gains while lending to strategies and distributes it to SLPs by updating the sanRate or locking the interests to be distributed for a short time amount of time before distributing it. This function can only be called by a PoolManager contract having some strategies associated.
  • gain: Interests accumulated from lending


function signalLoss(uint256 loss) external;
Takes into account a loss made by a yield farming strategy. This function is called by a PoolManager contract having some yield farming strategies associated. Fees are not accumulated for this function before being distributed: everything is directly used to decrease the sanRate.
  • loss: Loss made by the yield farming strategy


function setTargetHAHedge(uint256 _targetHAHedge) external;
Sets the proportion of stocksUsers available for perpetuals. This function can only be called by the PerpetualManager: it allows to maintain the consistency between the data in PerpetualManager and in StableMaster.


function convertToSLP(uint256 amount, address user) external;
Transforms a HA position into a SLP Position. This function can only be called by a PerpetualManager contract when a HA wishes to cash out but there is not enough collateral in reserves.
  • amount: The amount to transform
  • user: Address to mint sanTokens to


function getStocksUsers() external returns (uint256);
Transmits to the PerpetualManager the max amount HAs can cover.
Return Values:
  • stockUsers: The amount of stablecoins minted using the collateral corresponding to the PerpetualManager minus the amount of stablecoins that were burnt for this collateral.


function setFeeKeeper(IPoolManager manager, uint64 _feeMint, uint64 _feeBurn, uint64 _slippage, uint64 _slippageFee) external;
Updates all the fees not depending on personal agents inputs via a keeper calling the corresponding updateUsersSLPsfunction in the FeeManager contract. This function can hence only be called by an accepted FeeManager contract.
  • manager: Reference to the PoolManagercontract corresponding to the collateral of interest
  • _bonusMalusMint: New corrector of user mint fees for this collateral. These fees will correct the mint fees from users that just depend on the hedge curve by HAs by introducing other dependencies. In normal times they will be equal to BASE_PARAMS meaning fees will just depend on coverage.
  • _bonusMalusBurn: New corrector of user burn fees, depending on collateral ratio
  • _slippage: New global slippage (the SLP fees from withdrawing) factor
  • _slippageFee: New global slippage fee (the non distributed accumulated fees) factor

Core Functions

The StableMaster contract contains functions that allow the Core contract of the protocol handling all the stablecoins to change parameters in the StableMaster and then to have this contract propagate the changes to the AgToken contract and to all the collateral managers. The PoolManager contracts then propagate changes to the contracts of the same pair stablecoin/collateral they are associated to like the PerpetualManager.
These functions include:
  • addGovernor(): to add a new governor address to the governor list
  • removeGovernor(): to remove an address from the governor list
  • setGuardian(): to change the guardian address
  • revokeGuardian(): to revoke the guardian address
  • setCore(): to change the reference to the _core address of the StableMaster

Governor Functions

While some changes can directly be propagated by the Core contract, governance has the ability to directly modify some parameters and call some functions in the StableMaster contract, especially functions regarding a given collateral.


function deployCollateral(IPoolManager poolManager, IPerpetualManager perpetualManager, IFeeManager feeManager, IOracle oracle, ISanToken sanToken) external;
Deploys a new collateral by creating the correct references in the corresponding contracts. Some parameters that will then be stored in the collateralMap about the deployed collateral can directly be retrieved from the other contracts passed as reference in this function. This is the case for the associated token which is already stored in the poolManager contract.
  • poolManager: Contract managing and storing the deployed collateral for this stablecoin
  • perpetualManager: Contract managing HA perpetuals for this stablecoin/collateral pair
  • oracle: Reference to the oracle that will give the price of the collateral with respect to the stablecoin
  • sanToken: Reference to the sanTokens associated to the collateral


function revokeCollateral(IPoolManager poolManager, ICollateralSettler settlementContract) external;
Removes a collateral from the list of accepted collateral types and pauses all actions associated to this collateral. The reason for this function being only governor is that it has the ability to transfer the contract's funds to another contract.
Before calling this function, governance should make sure that all the collateral lent to strategies has been withdrawn.
  • poolManager: Reference to the contract managing this collateral for this stablecoin in the protocol
  • settlementContract: Settlement contract that will be used to close everyone's positions and to let users, SLPs and HAs redeem if not all a portion of their claim


function setOracle(IOracle _oracle, IPoolManager poolManager) external;
Propagates the change of oracle for one collateral to all the contracts which need to have the correct oracle reference. This function can only be called by the governor because it could be used to manipulate prices at the advantage of some stakeholders of the protocol while disadvantaging others.

Guardian Functions

Some parameters may need to be changed rapidly in case of unexpected situations. To this extent, the guardian of the protocol needs to be able to change these parameters. The following functions can only be called by the guardian (or by any other governor address).


function pause(bytes32 agent, IPoolManager poolManager) external;
Pauses an agent's actions within this contract for a given collateral type for this stablecoin
If agent is STABLE, it is going to be impossible for users to mint stablecoins using the collateral specified by poolManageror to burn their stablecoins against the collateral specified by poolManager. If agent is SLP, it is going to be impossible for SLPs to deposit the collateral corresponding to poolManager and receive sanTokens in exchange, or to withdraw the same collateral from their sanTokens
  • agent: Bytes representing the agent (SLP or STABLE) and the collateral type that is going to be paused. To get the bytes32 from a string, we use in Solidity a keccak256 function
  • poolManager: Reference to the contract managing this collateral for this stablecoin in the protocol and for which agent needs to be paused


function unpause(bytes32 agent, IPoolManager poolManager) external;
Unpauses an agent's action for a given collateral type for this stablecoin. Before calling this function, the agent should have been paused for this collateral.
  • agent: Agent (SLP or STABLE) to unpause the action of
  • poolManager: Reference to the associated PoolManager


function rebalanceStocksUsers(uint256 amount, IPoolManager poolManagerUp, IPoolManager poolManagerDown) external;
Updates the stocksUsers for a given pair of collateral. This function can be called in case where the reserves of the protocol for each collateral do not exactly match what is stored in the stocksUsers because of increases or decreases in collateral prices at times in which the protocol was not fully covered by HAs.
  • amount: Amount of stocksUsers to transfer from a pool to another
  • poolManagerUp: Reference to PoolManager for which stocksUsers needs to increase
  • poolManagerDown Reference to PoolManager for which stocksUsers needs to decrease

Setter Functions

There are many setter functions to change parameters which can only be called by a GUARDIAN_ROLE. Each of these functions take a PoolManager contract as parameter:
  • setUserFees: Sets the x array (ie thresholds of hedge ratio) and the y array (ie values of fees at thresholds) used to compute mint and burn fees for users
  • setCapOnStableAndMaxInterests: Changes the parameters to cap the number of stablecoins you can issue using one collateral type and the maximum interests you can distribute to SLPs in a sanRate update in a block
  • setIncentivesForSLPs: Sets the proportion of fees from burn/mint of users and of lending interests going to SLPs. The higher these proportions the bigger the APY for SLPs
  • setFeeManager: Sets a new FeeManager contract for a given collateral and removes the old one which becomes useless