Skip to content

Commit ffc90a8

Browse files
Single sided deposit for Billy (#218)
* single sided deposit for billy * Apply suggestions from code review Co-authored-by: Brandon Iles <[email protected]> * updated slippage condition * Added events to keep track of swaps offchain --------- Co-authored-by: Brandon Iles <[email protected]>
1 parent 596b353 commit ffc90a8

File tree

6 files changed

+952
-88
lines changed

6 files changed

+952
-88
lines changed

spot-vaults/contracts/BillBroker.sol

Lines changed: 188 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ import { UnacceptableSwap, UnreliablePrice, UnexpectedDecimals, InvalidPerc, Inv
5050
* Lenders can buy perps from the bill broker contract when it's under-priced,
5151
* hold the perp tokens until the market price recovers and sell it back to the bill broker contract.
5252
*
53+
* Single Sided deposits:
54+
* The pool also supports single sided deposits with either perps or usd tokens
55+
* insofar as it brings the pool back into balance.
5356
*
5457
*/
5558
contract BillBroker is
@@ -112,6 +115,29 @@ contract BillBroker is
112115
/// the swap fees transition from a flat percentage fee to a linear function.
113116
Range public arSoftBound;
114117

118+
//--------------------------------------------------------------------------
119+
// Events
120+
121+
/// @notice Emitted when a user deposits USD tokens to mint LP tokens.
122+
/// @param usdAmtIn The amount of USD tokens deposited.
123+
/// @param preOpState The reserve state before the deposit operation.
124+
event DepositUSD(uint256 usdAmtIn, ReserveState preOpState);
125+
126+
/// @notice Emitted when a user deposits Perp tokens to mint LP tokens.
127+
/// @param perpAmtIn The amount of Perp tokens deposited.
128+
/// @param preOpState The reserve state before the deposit operation.
129+
event DepositPerp(uint256 perpAmtIn, ReserveState preOpState);
130+
131+
/// @notice Emitted when a user swaps Perp tokens for USD tokens.
132+
/// @param perpAmtIn The amount of Perp tokens swapped in.
133+
/// @param preOpState The reserve state before the swap operation.
134+
event SwapPerpsForUSD(uint256 perpAmtIn, ReserveState preOpState);
135+
136+
/// @notice Emitted when a user swaps USD tokens for Perp tokens.
137+
/// @param usdAmtIn The amount of USD tokens swapped in.
138+
/// @param preOpState The reserve state before the swap operation.
139+
event SwapUSDForPerps(uint256 usdAmtIn, ReserveState preOpState);
140+
115141
//--------------------------------------------------------------------------
116142
// Modifiers
117143

@@ -289,6 +315,90 @@ contract BillBroker is
289315
_mint(msg.sender, mintAmt);
290316
}
291317

318+
/// @notice Single sided usd token deposit and mint LP tokens.
319+
/// @param usdAmtIn The amount of usd tokens to be deposited.
320+
/// @param postOpAssetRatioMax The system asset ratio can be no higher than this value after deposit.
321+
/// @return mintAmt The amount of LP tokens minted.
322+
function depositUSD(
323+
uint256 usdAmtIn,
324+
uint256 postOpAssetRatioMax
325+
) external nonReentrant whenNotPaused returns (uint256 mintAmt) {
326+
ReserveState memory preOpState = reserveState();
327+
uint256 preOpAssetRatio = assetRatio(preOpState);
328+
uint256 postOpAssetRatio = assetRatio(
329+
ReserveState({
330+
usdBalance: preOpState.usdBalance + usdAmtIn,
331+
perpBalance: preOpState.perpBalance,
332+
usdPrice: preOpState.usdPrice,
333+
perpPrice: preOpState.perpPrice
334+
})
335+
);
336+
337+
// We allow minting only pool is underweight usd
338+
if (preOpAssetRatio >= ONE || postOpAssetRatio > ONE) {
339+
return 0;
340+
}
341+
342+
mintAmt = computeMintAmtWithUSD(usdAmtIn, preOpState);
343+
if (mintAmt <= 0) {
344+
return 0;
345+
}
346+
if (postOpAssetRatio > postOpAssetRatioMax) {
347+
revert SlippageTooHigh();
348+
}
349+
350+
// Transfer usd tokens from the user
351+
usd.safeTransferFrom(msg.sender, address(this), usdAmtIn);
352+
353+
// mint LP tokens to the user
354+
_mint(msg.sender, mintAmt);
355+
356+
// Emit deposit info
357+
emit DepositUSD(usdAmtIn, preOpState);
358+
}
359+
360+
/// @notice Single sided perp token deposit and mint LP tokens.
361+
/// @param perpAmtIn The amount of perp tokens to be deposited.
362+
/// @param postOpAssetRatioMin The system asset ratio can be no lower than this value after deposit.
363+
/// @return mintAmt The amount of LP tokens minted.
364+
function depositPerp(
365+
uint256 perpAmtIn,
366+
uint256 postOpAssetRatioMin
367+
) external nonReentrant whenNotPaused returns (uint256 mintAmt) {
368+
ReserveState memory preOpState = reserveState();
369+
uint256 preOpAssetRatio = assetRatio(preOpState);
370+
uint256 postOpAssetRatio = assetRatio(
371+
ReserveState({
372+
usdBalance: preOpState.usdBalance,
373+
perpBalance: preOpState.perpBalance + perpAmtIn,
374+
usdPrice: preOpState.usdPrice,
375+
perpPrice: preOpState.perpPrice
376+
})
377+
);
378+
379+
// We allow minting only pool is underweight perp
380+
if (preOpAssetRatio <= ONE || postOpAssetRatio < ONE) {
381+
return 0;
382+
}
383+
384+
mintAmt = computeMintAmtWithPerp(perpAmtIn, preOpState);
385+
if (mintAmt <= 0) {
386+
return 0;
387+
}
388+
if (postOpAssetRatio < postOpAssetRatioMin) {
389+
revert SlippageTooHigh();
390+
}
391+
392+
// Transfer perp tokens from the user
393+
perp.safeTransferFrom(msg.sender, address(this), perpAmtIn);
394+
395+
// mint LP tokens to the user
396+
_mint(msg.sender, mintAmt);
397+
398+
// Emit deposit info
399+
emit DepositPerp(perpAmtIn, preOpState);
400+
}
401+
292402
/// @notice Burns LP tokens and redeems usd and perp tokens.
293403
/// @param burnAmt The LP tokens to be burnt.
294404
/// @return usdAmtOut The amount usd tokens returned.
@@ -323,10 +433,11 @@ contract BillBroker is
323433
uint256 perpAmtMin
324434
) external nonReentrant whenNotPaused returns (uint256 perpAmtOut) {
325435
// compute perp amount out
436+
ReserveState memory preOpState = reserveState();
326437
uint256 protocolFeePerpAmt;
327438
(perpAmtOut, , protocolFeePerpAmt) = computeUSDToPerpSwapAmt(
328439
usdAmtIn,
329-
reserveState()
440+
preOpState
330441
);
331442
if (usdAmtIn <= 0 || perpAmtOut <= 0) {
332443
revert UnacceptableSwap();
@@ -345,6 +456,9 @@ contract BillBroker is
345456

346457
// transfer perps out to the user
347458
perp.safeTransfer(msg.sender, perpAmtOut);
459+
460+
// Emit swap info
461+
emit SwapUSDForPerps(usdAmtIn, preOpState);
348462
}
349463

350464
/// @notice Swaps perp tokens from the user for usd tokens from the reserve.
@@ -356,11 +470,9 @@ contract BillBroker is
356470
uint256 usdAmtMin
357471
) external nonReentrant whenNotPaused returns (uint256 usdAmtOut) {
358472
// Compute swap amount
473+
ReserveState memory preOpState = reserveState();
359474
uint256 protocolFeeUsdAmt;
360-
(usdAmtOut, , protocolFeeUsdAmt) = computePerpToUSDSwapAmt(
361-
perpAmtIn,
362-
reserveState()
363-
);
475+
(usdAmtOut, , protocolFeeUsdAmt) = computePerpToUSDSwapAmt(perpAmtIn, preOpState);
364476
if (perpAmtIn <= 0 || usdAmtOut <= 0) {
365477
revert UnacceptableSwap();
366478
}
@@ -378,11 +490,30 @@ contract BillBroker is
378490

379491
// transfer usd out to the user
380492
usd.safeTransfer(msg.sender, usdAmtOut);
493+
494+
// Emit swap info
495+
emit SwapPerpsForUSD(perpAmtIn, preOpState);
381496
}
382497

383498
//-----------------------------------------------------------------------------
384499
// Public methods
385500

501+
/// @notice Computes the amount of LP tokens minted,
502+
/// when the given number of usd tokens are deposited.
503+
/// @param usdAmtIn The amount of usd tokens deposited.
504+
/// @return mintAmt The amount of LP tokens minted.
505+
function computeMintAmtWithUSD(uint256 usdAmtIn) public returns (uint256 mintAmt) {
506+
return computeMintAmtWithUSD(usdAmtIn, reserveState());
507+
}
508+
509+
/// @notice Computes the amount of LP tokens minted,
510+
/// when the given number of perp tokens are deposited.
511+
/// @param perpAmtIn The amount of perp tokens deposited.
512+
/// @return mintAmt The amount of LP tokens minted.
513+
function computeMintAmtWithPerp(uint256 perpAmtIn) public returns (uint256 mintAmt) {
514+
return computeMintAmtWithPerp(perpAmtIn, reserveState());
515+
}
516+
386517
/// @notice Computes the amount of usd tokens swapped out,
387518
/// when the given number of perp tokens are sent in.
388519
/// @param perpAmtIn The amount of perp tokens swapped in.
@@ -508,6 +639,58 @@ contract BillBroker is
508639
mintAmt = mintAmt.mulDiv(ONE - fees.mintFeePerc, ONE);
509640
}
510641

642+
/// @notice Computes the amount of LP tokens minted,
643+
/// when the given number of usd tokens are deposited.
644+
/// @param usdAmtIn The amount of usd tokens deposited.
645+
/// @param s The current reserve state.
646+
/// @return mintAmt The amount of LP tokens minted.
647+
function computeMintAmtWithUSD(
648+
uint256 usdAmtIn,
649+
ReserveState memory s
650+
) public view returns (uint256) {
651+
if (usdAmtIn <= 0) {
652+
return 0;
653+
}
654+
655+
uint256 valueIn = s.usdPrice.mulDiv(usdAmtIn, usdUnitAmt);
656+
uint256 totalReserveVal = (s.usdPrice.mulDiv(s.usdBalance, usdUnitAmt) +
657+
s.perpPrice.mulDiv(s.perpBalance, perpUnitAmt));
658+
659+
return
660+
(totalReserveVal > 0)
661+
? valueIn.mulDiv(totalSupply(), totalReserveVal).mulDiv(
662+
ONE - fees.mintFeePerc,
663+
ONE
664+
)
665+
: 0;
666+
}
667+
668+
/// @notice Computes the amount of LP tokens minted,
669+
/// when the given number of perp tokens are deposited.
670+
/// @param perpAmtIn The amount of perp tokens deposited.
671+
/// @param s The current reserve state.
672+
/// @return mintAmt The amount of LP tokens minted.
673+
function computeMintAmtWithPerp(
674+
uint256 perpAmtIn,
675+
ReserveState memory s
676+
) public view returns (uint256) {
677+
if (perpAmtIn <= 0) {
678+
return 0;
679+
}
680+
681+
uint256 valueIn = s.perpPrice.mulDiv(perpAmtIn, perpUnitAmt);
682+
uint256 totalReserveVal = (s.usdPrice.mulDiv(s.usdBalance, usdUnitAmt) +
683+
s.perpPrice.mulDiv(s.perpBalance, perpUnitAmt));
684+
685+
return
686+
(totalReserveVal > 0)
687+
? valueIn.mulDiv(totalSupply(), totalReserveVal).mulDiv(
688+
ONE - fees.mintFeePerc,
689+
ONE
690+
)
691+
: 0;
692+
}
693+
511694
/// @notice Computes the amount of usd and perp tokens redeemed,
512695
/// when the given number of LP tokens are burnt.
513696
/// @param burnAmt The amount of LP tokens to be burnt.

spot-vaults/hardhat.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export default {
7878
enabled: !!process.env.REPORT_GAS,
7979
excludeContracts: ["_test/"],
8080
coinmarketcap: process.env.COINMARKETCAP_API_KEY,
81+
L1Etherscan: process.env.ETHERSCAN_API_KEY,
8182
},
8283
etherscan: {
8384
apiKey: process.env.ETHERSCAN_API_KEY,

spot-vaults/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
"ethers": "^6.6.0",
6363
"ethers-v5": "npm:ethers@^5.7.0",
6464
"ganache-cli": "latest",
65-
"hardhat": "^2.22.6",
65+
"hardhat": "^2.22.8",
6666
"hardhat-gas-reporter": "latest",
6767
"lodash": "^4.17.21",
6868
"prettier": "^2.7.1",

0 commit comments

Comments
 (0)