Published in


Post Mortem — Uncollateralized Minting of PAR tokens

On Friday, October 15th, we realized that one address had been minting PARs since Tuesday 12th. In total, 560,010 PARs have been minted before we could prevent further rogue minting on Friday night.

The address was one of our deployer keys and had been used to deploy and configure the protocol on Polygon.

Short form:


The incident was a minting exploit, meaning the attacker could use the deployer key to make the protocol mint PAR without collateral. There was no additional impact, and the vaults are safe.


We have decided to mint PAR and burn them to return the protocol to a state which satisfies the predicate written in the white paper, that all the outstanding PAR are backed by collateral in a vault. In other words, the company decided to cover the loss for a faulty deployment of the protocol on Polygon.


The issue happened because of a series of three mistakes we made during our deployment on Polygon.

1) Constructor

One deployer key is generated and used for the purpose of deploying the protocol.

As you can see in this audited code that the constructor provides the three following rights:


constructor() public {_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);_setupRole(MANAGER_ROLE, msg.sender);_setupRole(MINTER_ROLE, msg.sender);}
  • DEFAULT_ADMIN_ROLE was needed to grant roles to others.
  • MANAGER_ROLE was used for the initial configuration of the collateral and price feeds.
  • MINTER_ROLE, which is never used by the deployer key, should not be granted. We have already pushed a fix and removed it from the constructor.

2) Deployment

Upon deploying the contract, both the DEFAULT_ADMIN_ROLE and MANAGER_ROLE were handed over to the company’s multisignature wallet:

After which the deployer revoked those two roles from itself:

The MINTING_ROLE was never needed, and unfortunately, our deployment process failed to revoke it on Polygon.

To prevent this from happening again, we are updating our control script to cover the entire protocol configuration, including the ops part; our deployment process, and the tooling we have built to stabilize PAR.

3) Key Storage

As the key was not meant to be used for anything other than this deployment, it was not a candidate for secure storage, which allowed an attacker to take advantage of it.

To solve this issue, we are going to manage the disposable keys as we manage other keys: storing them in an encrypted vault.

Timeline (UTC time):

  • 2021–10–12_1655: The first time the rogue minting happened
  • 2021–10–15_1154: The last time the rogue minting happened
  • 2021–10–15_1415: Alerted by the community, a CM raises attention to a key with an unusual activity (minting, burning, swapping)
  • 2021–10–15_1500: The engineering department starts to investigate to explain the activity and does some checks while waiting for the answer from the engineer who deployed the protocol. The address is ours, used to deploy the protocol, has the minting right from the beginning, and is hardcoded in the stabilizer repo for a Polygon test.
  • 2021–10–16_0530: Transaction to revoke MINTER_ROLE from the deployer key initiated and confirmed 1 hour later

Fixes and Control

To fix the issue and rebalance the protocol, the first step was to revoke the MINTER_ROLE from the deployer address. The second and last step was to burn the extra PARs to fulfill the promise of never having PAR minted other than against collateral.

Here are the transactions made:

In the AccessController using the getRoleMember(role, index) function we can verify there are currently two addresses that have the MINTER_ROLE.

First, the VaultsCore: https://polygonscan.com/address/0x03175c19CB1d30Fa6060331A9ec181e04CAC6aB0

The VaultsCore needs minting rights whenever someone borrows PAR it can mint or when they repay it can burn it.

Second, the FeeDistributor: https://polygonscan.com/address/0x313d1d48430721370EcC57262A7664E375a347Fb

The FeeDistributor mints the PAR that the protocol has earned as income from the fees and pays it out to the payees in the contract, which in this case is the VaultsCore which acts as the safety reserve.


Detecting the issue faster would have allowed us to greatly reduce the impact. Lessons were learned. Audits aren’t everything. We are going to multiply reviews and strengthen processes to ensure that things like non-used rights won’t be granted. We are also already focusing on upgrading our tooling to control and monitor the protocol on Polygon.

The attacker was careful enough not to mint too many PAR tokens and destabilize the price, which delayed the findings of the situation. However, nothing else was impacted, and users’ vaults are safe.

We would like to thank the community for alerting the team of this issue as well as supporting the team during the settlement of this event.

The Mimo Team

Erratum & More detailed analysis


To go deeper into what happened with the deployer key, here is a transaction analysis (the attacker’s address is 0x1d8abea6d63fc48dc517edf8f6026edba4b61dd4)


| | |
| 0x54d06662d17fb810352886ef3431 | 10.00 |
| eba84a7331e94d6cc8ea18b3c053e3 | |
| | |
| 0xb5831b2ba3b62d35e1ba749bd9de | 50'000.00 |
| e9ae3ebc98f72fdc356a38e343728a | |
| | |
| 0xafc264f7cea92b9837d21a73d54f | 75'000.00 |
| 771098447e97bcbfef207db6eefa22 | |
| | |
| 0x66a445541ec02810c3206514b7d8 | 50'000.00 |
| 3d1c8d15ea55ee4f059d8949226cde | |
| | |
| 0x50ba3826b3af94a835b8d9550d92 | 25'000.00 |
| 1c022cc3b87682886a449213b06e33 | |
| | |
| 0xa9a2dce980bea4a67b8dc36fc630 | 50'000.00 |
| 8bfa3eb64a4c76a7f15e0542d3a1b9 | |
| | |
| 0x8a261b9eff8a1d2534e015bed07f | 250'000.00 |
| a9879f2b33be740c838a726caa6151 | |
| | |
| 0xc5e0768c24230358f512a20ba37f | 20'000.00 |
| 2ad0a9c8e04fc14c46c1efbd4ed58f | |
| | |
| 0x5151c723aaec0bff9c94c2837ddd | 25'000.00 |
| 14d73236623975ffd619daa5577370 | |
| | |
| 0xb0b97ef129c2be9c00d63ec30061 | 25'000.00 |
| 5d5144b15b1f1e044acb2a6a60ab50 | |
| | |
| 0xc78e9be0397482e2f7007ebf2597 | 250'000.00 |
| 94ca83f546bc016df21a54d991bd0f | |
| | |
| 0x74cca03bc5f1fe280eb45b4a09bf | 25'000.00 |
| c5f8ffd86a1a48efb31e209c670852 | |
| | |
| 0xde22254eeb54bc1c5f5f57d8cfa2 | 25'000.00 |
| baefe2e00cf7cb28396b32131be6c3 | |
| | |
| 0x351a799db733930adb591ea9528c | 50'000.00 |
| af72ba6a3cb9bfe9987421ef96c444 | |
| | |
| 0xef2be128d073cb626c5865d2d43e | 30'000.00 |
| 1e79acfa714e38bf725dceedbf85eb | |
| | |
| 0xc436f112af24966ce2b58076636e | 50'000.00 |
| 7f44fcfde4808bdbb5cf7aec4b3da9 | |
| | |
| 0x38487007d18d08aa0eba8c582471 | 10'000.00 |
| e4a797e31a51e760e226327c96d77b | |
| | |
| 0x741832d578310e53c51fe56c3189 | 100'000.00 |
| cf17c7da68c11e9b2bd7c108efcd91 | |
| | |
| Total | 1'110'010.00 |


| | | |
| 0x433abe2202795468d7d8cc2c1baa | 0x1d8...dd4 | 10.00 |
| ae0ee40fcbf72f4a2c363546d743bf | | |
| | | |
| 0xe25b6797b8fc991b683a925653e1 | 0x1d8...dd4 | 225'000.00 |
| 710504709ff7353a395552f53b15b5 | | |
| | | |
| 0x54d72810eae3324590954e6a4e70 |*0xba1...2c8*| 10'000.00 |
| 1a822dabb622ae48b266d83de5f285 | | |
| | | |
| 0x8299a556a14ac5369acecef57b53 |*0xba1...2c8*| 90'000.00 |
| 15cf7b62a1e837ecba2c62dd71bce8 | | |
| | | |
| 0xcbce039e9d7a8fa9f3e2f601d47c | 0x1d8...dd4 | 225'000.00 |
| b030d7de1b0b0094a0e3846f911099 | | |
| | | |
| Total | | 550'010.00 |


The attacker minted 1,110,010.00 PAR without collateral and burnt 550,010.00 of them.

  • We have burnt 560,010.00 PAR (meaning we’ve burnt 10 PAR too many, which will naturally become income for the protocol).
  • We have overlooked in our original analysis that two burn transactions issued by the deployer key were burning PAR deposited in a different address, namely the address of the Balancer Polygon pool. This doesn’t impact the balance of our protocol, nor the peg or PAR, but does affect the owner of this address, and therefore needs to be remediated.

The case of Balancer

If we sum the PAR liquidity from the web app across the 3 PAR pools and compare it and the actual liquidity from the pool, you can see that it’s off by 100k PAR.

Pool balance:(~1,735,726.5)


PAR/PAR (~40.0133)


jEUR/EURS/PAR (~711.8688)


USDC/PAR: (~1,834,975)


(40 + 711 + 1,834,975) — 1,735,726 = 100,000 (rounded numbers)

We have already contacted Balancer, and Mimo as a company will cover this by sending the exact amount of PAR burnt to the pool.

Unrelated considerations: the peg of PAR on Polygon

Some users have reached out to us thinking that the peg of PAR on Polygon was off because of the uncollateralized minting. The actual reason is not the attack nor the discrepancy on Balancer; it’s simply because the selling pressure has recently been very high. Users have been minting a lot of PAR and selling them as of recently.

We believe that the attacker burnt PARs from Balancer in an attempt to increase the price and sell with more profit. But unlike other DEXs that would have used on-chain balances, Balancer V2 doesn’t use erc20.balanceOf() anywhere, and instead uses its own internal accounting based on swaps, additions, and removals of liquidity to define the prices of assets. If the two burns on Balancer were impacting the peg, it would have been for a higher price instead of a lower price since there would be less PAR in the pool.

For information purposes, we will issue the following proposal to work on the peg:

  • Increase the minting fees on Polygon,
  • Increase liquidity providing incentive on PAR/USDC Polygon.
  • Lowering the minting incentives on Polygon.

Conclusion (again!)

Since the fix described in our original post, which is first removing the MINTER_ROLE from the deployer address and then burning the extra PARs, the protocol was back to normal with the right balance between the debt and the collateral. That said, we missed an element in the attacker’s behaviour. Even though the said element has no impact either on the protocol or on the peg, it is important to act on it. One person from the community alerted us suggesting we burn an extra 100,000 PAR, but the protocol accounting was correct, which led us to be confused.

After a more profound analysis, we realized that two transactions were missing from the report; The two burn transactions from Balancer for a total of 100,000 PAR. We could make things right with Balancer thanks to this person, even though we didn’t pick the suggested solution of burning the PAR.

The learning here is that even though the protocol was fixed, it was important to be thorough with our analysis or we could end up only partially remediating the situation. This will help us build more robust monitoring and testing tools.

For their instrumental participation in the resolution of this issue, we would like to thank McNultyFR, TokenBrice, Pascal at Jarvis, and the independent work of the Defi France community.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store


Accelerate the world into the future of sound money