Skip to content

Commit

Permalink
Peripheral Balance => Buffer Balance
Browse files Browse the repository at this point in the history
  • Loading branch information
Bridgerz committed Dec 15, 2023
1 parent 3cf7794 commit 9a63eab
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 99 deletions.
3 changes: 1 addition & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
MNEMONIC="test test test test test test test test test test test test"
INFURA_API_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
SWAP_ROUTER_ADDRESS=0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
INFURA_API_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
120 changes: 59 additions & 61 deletions contracts/AssurancePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,20 @@ contract AssurancePool is IAssurancePool, OwnableUpgradeable, ReentrancyGuardUpg
IStableCredit public stableCredit;
IERC20Upgradeable public reserveToken;
IAssuranceOracle public assuranceOracle;
// reserve token address => Reserve data
mapping(address => Reserve) public reserves;

/// @notice The primary reserve directly contributes to the current RTD calculation and
/// exists only to be used to cover reimbursements.
/// @dev reserve token address => primary reserve balance
mapping(address => uint256) public primaryReserve;
/// @notice The buffer reserve does not contribute to the current RTD calculation and
/// is used to cover reimbursements before the primary reserve is used.
/// @dev reserve token address => buffer reserve balance
mapping(address => uint256) public bufferReserve;
/// @notice the excess reserve does not contribute to the current RTD calculation and
/// is used to provide an overflow for deposits that would otherwise exceed the target RTD.
/// Operator access granted addresses can withdraw from the excess reserve.
/// @dev reserve token address => excess reserve balance
mapping(address => uint256) public excessReserve;

/* ========== INITIALIZER ========== */

Expand All @@ -44,7 +56,7 @@ contract AssurancePool is IAssurancePool, OwnableUpgradeable, ReentrancyGuardUpg
/// @notice returns the total amount of reserve tokens in the primary and peripheral reserves.
/// @return total amount of reserve tokens in the primary and peripheral reserves.
function reserveBalance() public view returns (uint256) {
return primaryBalance() + peripheralBalance();
return primaryBalance() + bufferBalance();
}

/// @notice returns the ratio of primary reserve to total debt, where 1 ether == 100%.
Expand Down Expand Up @@ -122,24 +134,19 @@ contract AssurancePool is IAssurancePool, OwnableUpgradeable, ReentrancyGuardUpg
: ((reserveAmount / 10 ** (reserveDecimals - creditDecimals)));
}

/// @notice returns the amount of current reserve token's unallocated balance.
function unallocatedBalance() public view returns (uint256) {
return reserves[address(reserveToken)].unallocatedBalance;
}

/// @notice returns the amount of current reserve token's primary balance.
function primaryBalance() public view returns (uint256) {
return reserves[address(reserveToken)].primaryBalance;
return primaryReserve[address(reserveToken)];
}

/// @notice returns the amount of current reserve token's peripheral balance.
function peripheralBalance() public view returns (uint256) {
return reserves[address(reserveToken)].peripheralBalance;
/// @notice returns the amount of current reserve token's buffer balance. The buffer balance
function bufferBalance() public view returns (uint256) {
return bufferReserve[address(reserveToken)];
}

/// @notice returns the amount of current reserve token's excess balance.
function excessBalance() public view override returns (uint256) {
return reserves[address(reserveToken)].excessBalance;
return excessReserve[address(reserveToken)];
}

/* ========== MUTATIVE FUNCTIONS ========== */
Expand All @@ -149,40 +156,53 @@ contract AssurancePool is IAssurancePool, OwnableUpgradeable, ReentrancyGuardUpg
function depositIntoPrimaryReserve(uint256 amount) public {
require(amount > 0, "AssurancePool: Cannot deposit 0");
// add deposit to primary balance
reserves[address(reserveToken)].primaryBalance += amount;
primaryReserve[address(reserveToken)] += amount;
// collect reserve token deposit from caller
reserveToken.safeTransferFrom(_msgSender(), address(this), amount);
emit PrimaryReserveDeposited(amount);
}

/// @notice enables caller to deposit reserve tokens into the peripheral reserve.
/// @notice enables caller to deposit reserve tokens into the buffer reserve.
/// @param amount amount of reserve token to deposit.
function depositIntoPeripheralReserve(uint256 amount) public override nonReentrant {
function depositIntoBufferReserve(uint256 amount) public override nonReentrant {
require(amount > 0, "AssurancePool: Cannot deposit 0");
// add deposit to peripheral balance
reserves[address(reserveToken)].peripheralBalance += amount;
// add deposit to buffer reserve
bufferReserve[address(reserveToken)] += amount;
// collect reserve token deposit from caller
reserveToken.safeTransferFrom(_msgSender(), address(this), amount);
emit PeripheralReserveDeposited(amount);
emit BufferReserveDeposited(amount);
}

/// @notice enables caller to deposit reserve tokens into the excess reserve.
/// @param amount amount of reserve token to deposit.
function depositIntoExcessReserve(uint256 amount) public {
// collect remaining amount from caller
reserveToken.safeTransferFrom(_msgSender(), address(this), amount);
// deposit remaining amount into excess balance
reserves[address(reserveToken)].excessBalance += amount;
// deposit remaining amount into excess reserve
excessReserve[address(reserveToken)] += amount;
emit ExcessReserveDeposited(amount);
}

/// @notice enables caller to deposit reserve tokens into the excess reserve.
/// @notice enables caller to deposit reserve tokens to be allocated into the necessary reserve.
/// @param amount amount of deposit token to deposit.
function deposit(uint256 amount) public virtual override {
function deposit(uint256 amount) public virtual override nonReentrant {
// collect deposit tokens from caller
reserveToken.safeTransferFrom(_msgSender(), address(this), amount);
reserves[address(reserveToken)].unallocatedBalance += amount;
allocate();
// calculate reserves needed to reach target RTD
uint256 _neededReserves = neededReserves();
// if neededReserve is greater than amount, deposit full amount into primary reserve
if (_neededReserves > amount) {
primaryReserve[address(reserveToken)] += amount;
amount = 0;
return;
}
// deposit neededReserves into primary reserve
if (_neededReserves > 0) {
primaryReserve[address(reserveToken)] += _neededReserves;
amount -= _neededReserves;
}
// deposit remaining amount into excess reserve
excessReserve[address(reserveToken)] += amount;
return;
}

Expand All @@ -192,38 +212,16 @@ contract AssurancePool is IAssurancePool, OwnableUpgradeable, ReentrancyGuardUpg
require(amount > 0, "AssurancePool: Cannot withdraw 0");
require(amount <= excessBalance(), "AssurancePool: Insufficient excess reserve");
// reduce excess balance
reserves[address(reserveToken)].excessBalance -= amount;
excessReserve[address(reserveToken)] -= amount;
// transfer reserve token to caller
reserveToken.safeTransfer(_msgSender(), amount);
emit ExcessReserveWithdrawn(amount);
}

/// @notice enables caller to allocate unallocated reserve tokens into the needed reserve balance.
/// @dev this function should be called on a time frame to ensure collected deposits are allocated to the
/// necessary reserve balances.
function allocate() public nonReentrant {
// calculate reserves needed to reach target RTD
uint256 _neededReserves = neededReserves();
// if neededReserve is greater than amount, deposit full amount into primary reserve
if (_neededReserves > unallocatedBalance()) {
reserves[address(reserveToken)].primaryBalance += unallocatedBalance();
reserves[address(reserveToken)].unallocatedBalance = 0;
return;
}
// deposit neededReserves into primary reserve
if (_neededReserves > 0) {
reserves[address(reserveToken)].primaryBalance += _neededReserves;
reserves[address(reserveToken)].unallocatedBalance -= _neededReserves;
}
// deposit remaining amount into excess reserve
reserves[address(reserveToken)].excessBalance += unallocatedBalance();
reserves[address(reserveToken)].unallocatedBalance = 0;
}

/* ========== RESTRICTED FUNCTIONS ========== */

/// @notice Called by the stable credit implementation to reimburse an account.
/// If the amount is covered by the peripheral reserve, the peripheral reserve is depleted first,
/// If the amount is covered by the buffer reserve, the buffer reserve is depleted first,
/// followed by the primary reserve.
/// @dev The stable credit implementation should not expose this function to the public as it could be
/// exploited to drain the reserves.
Expand All @@ -238,20 +236,20 @@ contract AssurancePool is IAssurancePool, OwnableUpgradeable, ReentrancyGuardUpg
{
// if no reserves, return
if (reserveBalance() == 0) return 0;
// if amount is covered by peripheral, reimburse only from peripheral
if (amount < peripheralBalance()) {
reserves[address(reserveToken)].peripheralBalance -= amount;
// if amount is covered by buffer, reimburse only from buffer
if (amount < bufferBalance()) {
bufferReserve[address(reserveToken)] -= amount;
// check if total amount can be covered by reserve
} else if (amount < reserveBalance()) {
// use both reserves to cover amount
reserves[address(reserveToken)].primaryBalance -= amount - peripheralBalance();
reserves[address(reserveToken)].peripheralBalance = 0;
primaryReserve[address(reserveToken)] -= amount - bufferBalance();
bufferReserve[address(reserveToken)] = 0;
} else {
// use entire reserve to cover amount
uint256 reserveAmount = reserveBalance();
// empty both reserves
reserves[address(reserveToken)].peripheralBalance = 0;
reserves[address(reserveToken)].primaryBalance = 0;
bufferReserve[address(reserveToken)] = 0;
primaryReserve[address(reserveToken)] = 0;
// set amount to available reserves
amount = reserveAmount;
}
Expand All @@ -266,19 +264,19 @@ contract AssurancePool is IAssurancePool, OwnableUpgradeable, ReentrancyGuardUpg
function reallocateExcessBalance() public onlyOperator {
uint256 _neededReserves = neededReserves();
if (_neededReserves > excessBalance()) {
reserves[address(reserveToken)].primaryBalance += excessBalance();
reserves[address(reserveToken)].excessBalance = 0;
primaryReserve[address(reserveToken)] += excessBalance();
excessReserve[address(reserveToken)] = 0;
} else {
reserves[address(reserveToken)].primaryBalance += _neededReserves;
reserves[address(reserveToken)].excessBalance -= _neededReserves;
primaryReserve[address(reserveToken)] += _neededReserves;
excessReserve[address(reserveToken)] -= _neededReserves;
}
emit ExcessReallocated(excessBalance(), primaryBalance());
}

/// @notice This function allows the risk manager to set the reserve token.
/// @dev Updating the reserve token will not affect the stored reserves of the previous reserve token.
/// @param _reserveToken address of the new reserve token.
function setReserveToken(address _reserveToken) external onlyOperator {
function setReserveToken(address _reserveToken) external onlyAdmin {
reserveToken = IERC20Upgradeable(_reserveToken);
emit ReserveTokenUpdated(_reserveToken);
}
Expand Down
1 change: 0 additions & 1 deletion contracts/StableCredit/MutualCredit.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import "../interfaces/IMutualCredit.sol";
Expand Down
7 changes: 1 addition & 6 deletions contracts/StableCredit/StableCredit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ contract StableCredit is MutualCredit, IStableCredit {
uint256 reserveTokenAmount = assurancePool.convertStableCreditToReserveToken(amount);
assurancePool.reserveToken().transferFrom(_msgSender(), address(this), reserveTokenAmount);
assurancePool.reserveToken().approve(address(assurancePool), reserveTokenAmount);
assurancePool.depositIntoPeripheralReserve(reserveTokenAmount);
assurancePool.depositIntoBufferReserve(reserveTokenAmount);
_transfer(address(this), member, amount);
emit CreditBalanceRepaid(member, amount);
}
Expand Down Expand Up @@ -160,11 +160,6 @@ contract StableCredit is MutualCredit, IStableCredit {
_;
}

modifier onlyOperator() {
require(access.isOperator(_msgSender()), "StableCredit: Unauthorized caller");
_;
}

modifier senderIsMember(address sender) {
require(
access.isMember(sender) || access.isOperator(sender),
Expand Down
15 changes: 4 additions & 11 deletions contracts/interfaces/IAssurancePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,12 @@ pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";

interface IAssurancePool {
struct Reserve {
uint256 unallocatedBalance;
uint256 primaryBalance;
uint256 peripheralBalance;
uint256 excessBalance;
}

/// @notice enables caller to deposit reserve tokens into the excess reserve.
/// @param amount amount of deposit token to deposit.
function deposit(uint256 amount) external;

/// @notice Called by the stable credit implementation to reimburse an account from the credit token's
/// reserves. If the amount is covered by the peripheral reserve, the peripheral reserve is depleted first,
/// reserves. If the amount is covered by the buffer reserve, the buffer reserve is depleted first,
/// followed by the primary reserve.
/// @dev The stable credit implementation should not expose this function to the public as it could be
/// exploited to drain the stable credit's reserves.
Expand All @@ -26,9 +19,9 @@ interface IAssurancePool {
function reimburse(address account, uint256 amount) external returns (uint256);

/// @notice enables caller to deposit a given reserve token into a stable credit's
/// peripheral reserve.
/// buffer reserve.
/// @param amount amount of reserve token to deposit.
function depositIntoPeripheralReserve(uint256 amount) external;
function depositIntoBufferReserve(uint256 amount) external;

/// @notice this function reallocates needed reserves from the excess reserve to the
/// primary reserve to attempt to reach the target RTD.
Expand Down Expand Up @@ -59,7 +52,7 @@ interface IAssurancePool {

event ExcessReallocated(uint256 excessReserve, uint256 primaryReserve);
event PrimaryReserveDeposited(uint256 amount);
event PeripheralReserveDeposited(uint256 amount);
event BufferReserveDeposited(uint256 amount);
event ExcessReserveDeposited(uint256 amount);
event ExcessReserveWithdrawn(uint256 amount);
event AccountReimbursed(address account, uint256 amount);
Expand Down
5 changes: 3 additions & 2 deletions tasks/demoSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,10 @@ task(DEMO_SETUP, "Configure a referenced network with demo tx's").setAction(
await (await stableCreditD.transfer(accountE.address, parseStableCredits("2500"))).wait()

// grant operator to Request ERC20 Proxy
const erc20Proxy = "0x2C2B9C9a4a25e24B174f26114e8926a9f2128FE4"
// const erc20Proxy = "0x2C2B9C9a4a25e24B174f26114e8926a9f2128FE4"

await (await accessManager.grantOperator(erc20Proxy)).wait()
// await (await accessManager.grantOperator(erc20Proxy)).wait()
await (await accessManager.grantOperator("0x358937c5674Fc7D3972Fa99AA7Ccdb8151EF1805")).wait()

console.log("🚀 demo configured")
}
Expand Down
27 changes: 13 additions & 14 deletions test/AssuranceTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,24 @@ contract AssuranceTest is StableCreditBaseTest {
// check primary reserve
assertEq(assurancePool.primaryBalance(), amount);
}
// deposit into peripheral reserve updates total reserve and peripheral reserve
// deposit into buffer reserve updates total reserve and buffer reserve

function testDepositIntoPeripheralReserve() public {
function testDepositIntoBufferReserve() public {
changePrank(deployer);
uint256 amount = 100;
// deposit into peripheral reserve
assurancePool.depositIntoPeripheralReserve(amount);
// deposit into buffer reserve
assurancePool.depositIntoBufferReserve(amount);
// check total reserve
assertEq(assurancePool.reserveBalance(), amount);
// check peripheral reserve
assertEq(assurancePool.peripheralBalance(), amount);
// check buffer reserve
assertEq(assurancePool.bufferBalance(), amount);
}

// deposit needed reserves updates excess pool when RTD is above target
function testAllocateReserveWithHighRTD() public {
changePrank(deployer);
assertEq(assurancePool.excessBalance(), 0);
assurancePool.deposit(100e6);
assurancePool.allocate();
// check excess reserve
assertEq(assurancePool.excessBalance(), 100e6);
}
Expand Down Expand Up @@ -82,16 +81,16 @@ contract AssuranceTest is StableCreditBaseTest {
assertEq(reserveToken.balanceOf(bob), 110e6);
}

function testReimburseAccountWithPrimaryAndPeripheralReserve() public {
function testReimburseAccountWithPrimaryAndBufferReserve() public {
changePrank(deployer);
// deposit into primary reserve
assurancePool.depositIntoPrimaryReserve(100e6);
// deposit into peripheral reserve
assurancePool.depositIntoPeripheralReserve(100e6);
// deposit into buffer reserve
assurancePool.depositIntoBufferReserve(100e6);
changePrank(address(stableCredit));
assurancePool.reimburse(bob, 10e6);
assertEq(assurancePool.primaryBalance(), 100e6);
assertEq(assurancePool.peripheralBalance(), 90e6);
assertEq(assurancePool.bufferBalance(), 90e6);
assertEq(reserveToken.balanceOf(bob), 110e6);
}

Expand Down Expand Up @@ -137,13 +136,13 @@ contract AssuranceTest is StableCreditBaseTest {
changePrank(deployer);
// deposit into primary reserve
assurancePool.depositIntoPrimaryReserve(25e6);
// deposit into peripheral reserve
assurancePool.depositIntoPeripheralReserve(25e6);
// deposit into buffer reserve
assurancePool.depositIntoBufferReserve(25e6);
changePrank(address(stableCredit));
assertEq(reserveToken.balanceOf(bob), 100e6);
assurancePool.reimburse(bob, 60e6);
assertEq(assurancePool.primaryBalance(), 0);
assertEq(assurancePool.peripheralBalance(), 0);
assertEq(assurancePool.bufferBalance(), 0);
assertEq(reserveToken.balanceOf(bob), 150e6);
}

Expand Down
Loading

0 comments on commit 9a63eab

Please sign in to comment.