Priceless Position Manager
Below, the GCR (“Global Collateralization Ratio”) is the ratio between the total amount of synthetic tokens in debt versus the total amount of collateral held in the contract. Note that this is only a ratio between token amounts, not token value; for example, a synthetic BTC with 1 sBTC of total synthetic debt and 1 USDC of collateral is actually 1.0, regardless of the price of Bitcoin.
State Variables
Note: we omit variables whose comments in Solidity do a good enough job of explaining their job.
contractState
is an enum with three possibilities:
Open
: “normal” pre-expiry operation. Users can start liquidations only when the EMP is in this state. Will advance whenexpire()
function is called afterexpirationTimestamp
(or whenemergencyShutdown
is called).ExpiredPriceRequested
: The contract’sexpire()
function has been successfully called and we are waiting for a returned OptimisticOracle price. Will advance whensettle()
is called if there is a resolved price available for the OptimisticOracle.ExpiredPriceReceived
: The OptimisticOracle price has resolved andsettle()
has been called at least once.
Keep in mind that the widely used _onlyPreExpiration
and _onlyPostExpiration
modifiers don’t look at this state directly; instead, they look at whether expirationTimestamp
has passed yet or not.
positions
is a mapping containing a PositionData
struct, one for each sponsor (note that it’s not possible for one sponsor to have two positions). These PositionData
structs track the sponsor’s outstandingTokens
(how much in synthetic assets they owe to the position), their collateral
(how much collateral is deposited), and information regarding withdrawal and transfer requests.
tokenCurrency
is the synthetic asset token address that was newly created upon EMP construction. This is the token in which debts are tallied, and all tokenCurreny
“out in the wild” correspond to some sponsor’s position with collateral, which initially minted them.
priceIdentifier
: This identifies what price the synthetic asset should follow. PriceIdentifier
must be one of the approved identifiers for the UMA system. As one example, the ETHUSD
price feed is an approved feed that dictates specifically how this price must be calculated. A special case is the price identifier NUMERICAL
, which relies on ancillaryData
described.
ancillaryData
: Carries additional information that might be necessary to calculate the price specified by priceIdentifier
. In the case of NUMERICAL, the ancillary data must pose a question that can be answered numerically. For more information, see Creating a Novel Synthetic Asset (using the NUMERICAL identifier).
withdrawalLiveness
: If a withdrawal would lower the GCR, it can’t be done instantly and must go through a waiting period. This variable defines how long that waiting period is. See the Solidity comments for more info.
minSponsorTokens
: If a user wants to become a sponsor, they must create a position that mints at least this many synthetic tokens, and can never go below this amount (except for paying it back in totality, thus removing it). This must be set to avoid any given position being so small that a liquidator is not incentivized to liquidate it (due to gas costs making it prohibitive).
It should be a considerable amount such that there is an incentive for the liquidator to liquidate it.
It should be easily mintable by a potential sponsor, i.e. it should not be a huge value.
The minSponsorTokens should make sense in collateral amount i.e. USDC. So the value should be, let’s say, worth 100 USDC or 10 USDC and so forth. It cannot be equal to 0.0001 USDC etc.
expiryPrice
: Only set while when contractState == ExpiredPriceResolved
. Stores the price the OptimisticOracle returned.
ooReward
: The amount of collateral that is sent to the OptimisticOracle as a reward to the proposer. This is also the bond that a liquidator or disputer must submit. During expiration, this cost is socialized to all sponsors to pay for the OptimisticOracle price request.
financialProductLibrary
: Always set to the zero address, and thus has no bearing on contract behaviour.
External/Public State-Changing Functions
requestTransferPosition
, transferPositionPassedRequest
, and cancelTransferPosition
all allow a sponsor to transfer his position to another address. The new address must not already hold a position. These methods use the withdrawalLiveness
value as a delay.
depositTo
and its wrapper deposit
simply transfer collateralAmount
collateral from the specified address to its sponsor position. This can be used to raise the CR of the position that is at risk of becoming undercollateralized.
Conversely, withdraw
allows a user to withdraw collateral from his position. In contrast to the below functions, this is instant, but can only be done if it does not result in the position having a lower CR than the GCR.
requestWithdrawal
, withdrawPassedRequest
, and cancelWithdrawal
behave similarly to the transferPosition function. One difference however is that a liquidation started on this position may essentially reset the countdown of the withdrawal. See Liquidatable
’s createLiquidation
function, near the end.
create
is where new synthetic tokens are born! The user deposits collateralAmount
collateral and mints numTokens
new synthetic tokens. He can set both of these parameters, but if this results in a sponsor position whose collateralization ratio is below the GCR, the call will fail. This ensures that new sponsors don’t ever move the total position closer to default. It will also fail if the position ends up with less than the minSponsorTokens
amount.
If successful, the user has a position with collateral in the collateral currency (usually USDC) and a debt of synthetic tokens (position.tokensOutstanding
); he also has newly-minted synthetic tokens equal to this debt in his wallet, which he may send away or do whatever he wants with.
repay
burns numTokens
synthetic asset tokens from the sponsor’s wallet, and reduces his position’s tokensOutstanding
value by the amount burned (unless this would result in a value of less than minSponsorTokens
, in which case the call reverts). Like deposit
, this can be used to increase the CR of the position, but this time by repaying some of the debt of the position.
redeem
also burns numTokens
synthetic asset tokens from the sponsor’s wallet and reduces tokensOutstanding
, but instead of lowering its CR, collateral is returned from the position to the sponsor’s wallet. This will also revert if it results in a position’s tokensOutstanding
going below minSponsorTokens
- unless the tokensOutstanding
goes to zero (meaning numTokens == tokensOutstanding
), in which case all collateral is returned and the position is deleted.
expire
can be called by anyone, and can only be called if contractState == Open
but the expirationTimestamp
has passed. This function requests a price from the optimistic oracle, socializes the cost of the request to the sponsors, and sets contractState
to ExpiredPriceRequested
.
emergencyShutdown
does essentially the same thing as expire
, but it must be called by the governor
and can be called before expirationTimestamp
has passed.
settleExpired
does a few different things. First, it checks that expire
has already been called and that the oracle’s price has now resolved. If these checks pass, the following effects take place:
If this is the first time the function has been successfully called, it will store the price from the OptimisticOracle and move the
contractState
toExpiredPriceReceived
.If the caller has any synthetic tokens in his wallet, these are burned and the caller is sent an equivalent amount of underlying collateral, according to the price. In other words, any synthetic tokens are “cashed out” for underlying collateral at market price.
If the caller has a sponsor position (and thus a debt of
position.tokensOutstanding
synthetic tokens), this debt is converted into a collateral amount and subtracted from the position’s collateral. Any leftover collateral is sent to the user.
Internal State-Changing Functions
_reduceSponsorPosition
is not called within PricelessPositionManager
code directly, instead only used in Liquidatable
which inherits it. This function reduces a given sponsor’s position values for any of the passed variables for tokensToRemove
(here meaning synthetic token debt), collateralToRemove
, and withdrawalAmountToRemove
, which removes collateral from any pending slow withdrawal. This function will revert if this operation results in a position having less than minSponsorTokens
for tokensOutstanding
, except in the case where tokensOutstanding
and rawCollateral
would reach zero, in which case the position is deleted.
_incrementCollateralBalances
and _decrementCollateralBalances
simply add or subtract both from some local memory
argument and the global tracker totalPositionCollateral
, ensuring consistency between individual and global variables.
_decrementCollateralBalancesCheckGCR
does the same as the previous decrement function, but will revert if the resulting position CR is lower than the GCR.
_requestOraclePrice_senderPays
does the same thing as the previous function, but is used by the Liquidatable.dispute
function and gets ooReward
from msg.sender
, rather than socializing the cost to sponsors. In other words, while the previous function charges “the EMP itself” (and all its sponsors) for the cost, this function expects msg.sender
to pay.
_getOraclePrice
gets a price that has been resolved, after either of the two above functions have requested a price. If the price has not yet resolved the function will revert.
Both _transformPrice
and _transformPriceIdentifier
simply return the price or identifier respectively, since financialContractLibrary
is the zero address for the EMP.
Last updated