From e2cfcd959e7356ffff828d2d09928fe4c58744f2 Mon Sep 17 00:00:00 2001 From: aalavandhann <6264334+aalavandhan@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:12:03 -0400 Subject: [PATCH 1/4] single sided deposit for billy --- spot-vaults/contracts/BillBroker.sol | 147 +++++ spot-vaults/test/BillBroker_deposit_redeem.ts | 620 +++++++++++++++++- 2 files changed, 766 insertions(+), 1 deletion(-) diff --git a/spot-vaults/contracts/BillBroker.sol b/spot-vaults/contracts/BillBroker.sol index 1efb986d..cc299ab5 100644 --- a/spot-vaults/contracts/BillBroker.sol +++ b/spot-vaults/contracts/BillBroker.sol @@ -50,6 +50,9 @@ import { UnacceptableSwap, UnreliablePrice, UnexpectedDecimals, InvalidPerc, Inv * Lenders can buy perps from the bill broker contract when it's under-priced, * hold the perp tokens until the market price recovers and sell it back to the bill broker contract. * + * Single Sided deposits: + * The pool also supports single sided deposits with either perps or usd tokens + * insofar as it brings the pool back into balance. * */ contract BillBroker is @@ -289,6 +292,56 @@ contract BillBroker is _mint(msg.sender, mintAmt); } + /// @notice Single sided usd token deposit and mint LP tokens. + /// @param usdAmtIn The amount of usd tokens to be deposited. + /// @param mintAmtMin The minimum amount of LP tokens expected to be minted. + /// @param mintAmtMax The maximum amount of LP tokens expected to be minted. + /// @return mintAmt The amount of LP tokens minted. + function depositUSD( + uint256 usdAmtIn, + uint256 mintAmtMin, + uint256 mintAmtMax + ) external nonReentrant whenNotPaused returns (uint256 mintAmt) { + mintAmt = computeMintAmtWithUSD(usdAmtIn, reserveState()); + if (mintAmt <= 0) { + return 0; + } + if (mintAmt < mintAmtMin || mintAmt > mintAmtMax) { + revert SlippageTooHigh(); + } + + // Transfer usd tokens from the user + usd.safeTransferFrom(msg.sender, address(this), usdAmtIn); + + // mint LP tokens to the user + _mint(msg.sender, mintAmt); + } + + /// @notice Single sided perp token deposit and mint LP tokens. + /// @param perpAmtIn The amount of perp tokens to be deposited. + /// @param mintAmtMin The minimum amount of LP tokens expected to be minted. + /// @param mintAmtMax The maximum amount of LP tokens expected to be minted. + /// @return mintAmt The amount of LP tokens minted. + function depositPerp( + uint256 perpAmtIn, + uint256 mintAmtMin, + uint256 mintAmtMax + ) external nonReentrant whenNotPaused returns (uint256 mintAmt) { + mintAmt = computeMintAmtWithPerp(perpAmtIn, reserveState()); + if (mintAmt <= 0) { + return 0; + } + if (mintAmt < mintAmtMin || mintAmt > mintAmtMax) { + revert SlippageTooHigh(); + } + + // Transfer perp tokens from the user + perp.safeTransferFrom(msg.sender, address(this), perpAmtIn); + + // mint LP tokens to the user + _mint(msg.sender, mintAmt); + } + /// @notice Burns LP tokens and redeems usd and perp tokens. /// @param burnAmt The LP tokens to be burnt. /// @return usdAmtOut The amount usd tokens returned. @@ -383,6 +436,22 @@ contract BillBroker is //----------------------------------------------------------------------------- // Public methods + /// @notice Computes the amount of LP tokens minted, + /// when the given number of usd tokens are deposited. + /// @param usdAmtIn The amount of usd tokens deposited. + /// @return mintAmt The amount of LP tokens minted. + function computeMintAmtWithUSD(uint256 usdAmtIn) public returns (uint256 mintAmt) { + return computeMintAmtWithUSD(usdAmtIn, reserveState()); + } + + /// @notice Computes the amount of LP tokens minted, + /// when the given number of perp tokens are deposited. + /// @param perpAmtIn The amount of perp tokens deposited. + /// @return mintAmt The amount of LP tokens minted. + function computeMintAmtWithPerp(uint256 perpAmtIn) public returns (uint256 mintAmt) { + return computeMintAmtWithPerp(perpAmtIn, reserveState()); + } + /// @notice Computes the amount of usd tokens swapped out, /// when the given number of perp tokens are sent in. /// @param perpAmtIn The amount of perp tokens swapped in. @@ -508,6 +577,84 @@ contract BillBroker is mintAmt = mintAmt.mulDiv(ONE - fees.mintFeePerc, ONE); } + /// @notice Computes the amount of LP tokens minted, + /// when the given number of usd tokens are deposited. + /// @param usdAmtIn The amount of usd tokens deposited. + /// @param s The current reserve state. + /// @return mintAmt The amount of LP tokens minted. + function computeMintAmtWithUSD( + uint256 usdAmtIn, + ReserveState memory s + ) public view returns (uint256 mintAmt) { + if (usdAmtIn <= 0) { + return 0; + } + + uint256 assetRatioPre = assetRatio(s); + uint256 assetRatioPost = assetRatio( + ReserveState({ + usdBalance: s.usdBalance + usdAmtIn, + perpBalance: s.perpBalance, + usdPrice: s.usdPrice, + perpPrice: s.perpPrice + }) + ); + + // We only allow minting LP-tokens with USD, + // when asset ratio is less than ONE; i.e) pool is overweight Perp. + if (assetRatioPre >= ONE || assetRatioPost >= ONE) { + return 0; + } + + uint256 valueIn = s.usdPrice.mulDiv(usdAmtIn, usdUnitAmt); + uint256 totalReserveVal = (s.usdPrice.mulDiv(s.usdBalance, usdUnitAmt) + + s.perpPrice.mulDiv(s.perpBalance, perpUnitAmt)); + + mintAmt = valueIn.mulDiv(totalSupply(), totalReserveVal).mulDiv( + ONE - fees.mintFeePerc, + ONE + ); + } + + /// @notice Computes the amount of LP tokens minted, + /// when the given number of perp tokens are deposited. + /// @param perpAmtIn The amount of perp tokens deposited. + /// @param s The current reserve state. + /// @return mintAmt The amount of LP tokens minted. + function computeMintAmtWithPerp( + uint256 perpAmtIn, + ReserveState memory s + ) public view returns (uint256 mintAmt) { + if (perpAmtIn <= 0) { + return 0; + } + + uint256 assetRatioPre = assetRatio(s); + uint256 assetRatioPost = assetRatio( + ReserveState({ + usdBalance: s.usdBalance, + perpBalance: s.perpBalance + perpAmtIn, + usdPrice: s.usdPrice, + perpPrice: s.perpPrice + }) + ); + + // We only allow minting LP-tokens with Perps, + // when asset ratio is greater than ONE; i.e) pool is overweight USD. + if (assetRatioPre <= ONE || assetRatioPost <= ONE) { + return 0; + } + + uint256 valueIn = s.perpPrice.mulDiv(perpAmtIn, perpUnitAmt); + uint256 totalReserveVal = (s.usdPrice.mulDiv(s.usdBalance, usdUnitAmt) + + s.perpPrice.mulDiv(s.perpBalance, perpUnitAmt)); + + mintAmt = valueIn.mulDiv(totalSupply(), totalReserveVal).mulDiv( + ONE - fees.mintFeePerc, + ONE + ); + } + /// @notice Computes the amount of usd and perp tokens redeemed, /// when the given number of LP tokens are burnt. /// @param burnAmt The amount of LP tokens to be burnt. diff --git a/spot-vaults/test/BillBroker_deposit_redeem.ts b/spot-vaults/test/BillBroker_deposit_redeem.ts index 2c460873..d8bcc630 100644 --- a/spot-vaults/test/BillBroker_deposit_redeem.ts +++ b/spot-vaults/test/BillBroker_deposit_redeem.ts @@ -221,6 +221,317 @@ describe("BillBroker", function () { }); }); + describe("#computeMintAmtWithUSD", function () { + describe("when usdAmtIn is zero", function () { + it("should return zero", async function () { + const { billBroker } = await loadFixture(setupContracts); + expect(await billBroker.computeMintAmtWithUSD.staticCall(0n)).to.eq(0n); + }); + }); + + describe("when total supply is zero", function () { + it("should return zero", async function () { + const { billBroker } = await loadFixture(setupContracts); + expect(await billBroker.computeMintAmtWithUSD.staticCall(usdFP("100"))).to.eq(0n); + }); + }); + + describe("when assetRatioPre > 1", function () { + it("should return zero", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("230")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("115"), + perpFP("100"), + usdFP("115"), + perpFP("100"), + ); + expect(await billBroker.computeMintAmtWithUSD.staticCall(usdFP("100"))).to.eq(0n); + }); + }); + + describe("when assetRatioPost > 1", function () { + it("should return zero", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("100")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("100"), + perpFP("100"), + usdFP("100"), + perpFP("100"), + ); + expect(await billBroker.computeMintAmtWithUSD.staticCall(usdFP("100"))).to.eq(0n); + }); + }); + + describe("when assetRatioPre = 1", function () { + it("should return the mint amount", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("115"), + perpFP("100"), + usdFP("115"), + perpFP("100"), + ); + expect(await billBroker.computeMintAmtWithUSD.staticCall(usdFP("1"))).to.eq(0n); + }); + }); + + describe("when assetRatioPost = 1", function () { + it("should return the mint amount", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("200")); + await billBroker.deposit( + usdFP("115"), + perpFP("200"), + usdFP("115"), + perpFP("200"), + ); + expect(await billBroker.computeMintAmtWithUSD.staticCall(usdFP("115"))).to.eq(0n); + }); + }); + + describe("when assetRatioPost < 1", function () { + it("should return the mint amount", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("200")); + await billBroker.deposit( + usdFP("115"), + perpFP("200"), + usdFP("115"), + perpFP("200"), + ); + expect(await billBroker.computeMintAmtWithUSD.staticCall(usdFP("11.5"))).to.eq( + lpAmtFP("10.5"), + ); + }); + }); + + describe("when fee > 0", function () { + it("should return the mint amount", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("200")); + await billBroker.deposit( + usdFP("115"), + perpFP("200"), + usdFP("115"), + perpFP("200"), + ); + await billBroker.updateFees({ + mintFeePerc: percentageFP("0.1"), + burnFeePerc: 0n, + perpToUSDSwapFeePercs: { + lower: 0n, + upper: 0n, + }, + usdToPerpSwapFeePercs: { + lower: 0n, + upper: 0n, + }, + protocolSwapSharePerc: 0n, + }); + expect(await billBroker.computeMintAmtWithUSD.staticCall(usdFP("11.5"))).to.eq( + lpAmtFP("9.45"), + ); + }); + }); + + describe("when the pool has only perps", function () { + it("should mint lp tokens", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("115"), + perpFP("100"), + usdFP("115"), + perpFP("100"), + ); + + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.swapPerpsForUSD(perpFP("100"), 0n); + expect(await usd.balanceOf(billBroker.target)).to.eq(0n); + + const s = await billBroker.reserveState.staticCall(); + expect( + await billBroker.assetRatio({ + usdBalance: s[0], + perpBalance: s[1], + usdPrice: s[2], + perpPrice: s[3], + }), + ).to.eq(0); + expect(await billBroker.computeMintAmtWithUSD.staticCall(usdFP("115"))).to.eq( + lpAmtFP("107.5"), + ); + }); + }); + }); + + describe("#computeMintAmtWithPerp", function () { + describe("when perpAmtIn is zero", function () { + it("should return zero", async function () { + const { billBroker } = await loadFixture(setupContracts); + expect(await billBroker.computeMintAmtWithPerp.staticCall(0n)).to.eq(0n); + }); + }); + + describe("when total supply is zero", function () { + it("should return zero", async function () { + const { billBroker } = await loadFixture(setupContracts); + expect(await billBroker.computeMintAmtWithPerp.staticCall(perpFP("100"))).to.eq( + 0n, + ); + }); + }); + + describe("when assetRatioPre < 1", function () { + it("should return zero", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("200")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("200"), + perpFP("100"), + usdFP("200"), + perpFP("100"), + ); + expect(await billBroker.computeMintAmtWithPerp.staticCall(perpFP("100"))).to.eq( + 0n, + ); + }); + }); + + describe("when assetRatioPost < 1", function () { + it("should return zero", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("120")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("120"), + perpFP("100"), + usdFP("120"), + perpFP("100"), + ); + expect(await billBroker.computeMintAmtWithPerp.staticCall(perpFP("100"))).to.eq( + 0n, + ); + }); + }); + + describe("when assetRatioPre = 1", function () { + it("should return the mint amount", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("115"), + perpFP("100"), + usdFP("115"), + perpFP("100"), + ); + expect(await billBroker.computeMintAmtWithPerp.staticCall(perpFP("1"))).to.eq(0n); + }); + }); + + describe("when assetRatioPost = 1", function () { + it("should return the mint amount", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("90")); + await billBroker.deposit(usdFP("115"), perpFP("90"), usdFP("115"), perpFP("90")); + expect(await billBroker.computeMintAmtWithPerp.staticCall(perpFP("10"))).to.eq( + 0n, + ); + }); + }); + + describe("when assetRatioPost > 1", function () { + it("should return the mint amount", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("200")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("200"), + perpFP("100"), + usdFP("200"), + perpFP("100"), + ); + expect(await billBroker.computeMintAmtWithPerp.staticCall(perpFP("10.5"))).to.eq( + lpAmtFP("11.5"), + ); + }); + }); + + describe("when fee > 0", function () { + it("should return the mint amount", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("200")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("200"), + perpFP("100"), + usdFP("200"), + perpFP("100"), + ); + await billBroker.updateFees({ + mintFeePerc: percentageFP("0.1"), + burnFeePerc: 0n, + perpToUSDSwapFeePercs: { + lower: 0n, + upper: 0n, + }, + usdToPerpSwapFeePercs: { + lower: 0n, + upper: 0n, + }, + protocolSwapSharePerc: 0n, + }); + expect(await billBroker.computeMintAmtWithPerp.staticCall(perpFP("10.5"))).to.eq( + lpAmtFP("10.35"), + ); + }); + }); + + describe("when the pool has only usd", function () { + it("should mint lp tokens", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("115"), + perpFP("100"), + usdFP("115"), + perpFP("100"), + ); + + await usd.approve(billBroker.target, usdFP("115")); + await billBroker.swapUSDForPerps(usdFP("115"), 0n); + expect(await perp.balanceOf(billBroker.target)).to.eq(0n); + + const s = await billBroker.reserveState.staticCall(); + expect( + await billBroker.assetRatio({ + usdBalance: s[0], + perpBalance: s[1], + usdPrice: s[2], + perpPrice: s[3], + }), + ).to.eq(ethers.MaxUint256); + expect(await billBroker.computeMintAmtWithPerp.staticCall(perpFP("100"))).to.eq( + lpAmtFP("107.5"), + ); + }); + }); + }); + describe("#deposit", function () { describe("when paused", function () { it("should revert", async function () { @@ -488,6 +799,313 @@ describe("BillBroker", function () { }); }); + describe("#depositUSD", function () { + describe("when paused", function () { + it("should revert", async function () { + const { billBroker } = await loadFixture(setupContracts); + await billBroker.pause(); + await expect(billBroker.depositUSD(usdFP("115"), lpAmtFP("100"), lpAmtFP("200"))) + .to.be.reverted; + }); + }); + + describe("when usdAmtIn is zero", function () { + it("should return zero", async function () { + const { billBroker } = await loadFixture(setupContracts); + const r = await billBroker.depositUSD.staticCall( + 0n, + lpAmtFP("100"), + lpAmtFP("200"), + ); + expect(r).to.eq(0n); + }); + }); + + describe("when slippage is too high", function () { + it("should revert if mintAmt is less than mintAmtMin", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("200")); + await billBroker.deposit( + usdFP("115"), + perpFP("200"), + usdFP("115"), + perpFP("200"), + ); + + await usd.approve(billBroker.target, usdFP("10")); + await expect( + billBroker.depositUSD(usdFP("10"), lpAmtFP("20"), lpAmtFP("30")), + ).to.be.revertedWithCustomError(billBroker, "SlippageTooHigh"); + }); + + it("should revert if mintAmt is greater than mintAmtMax", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("200")); + await billBroker.deposit( + usdFP("115"), + perpFP("200"), + usdFP("115"), + perpFP("200"), + ); + + await usd.approve(billBroker.target, usdFP("10")); + await expect( + billBroker.depositUSD(usdFP("10"), lpAmtFP("10"), lpAmtFP("20")), + ).to.be.revertedWithCustomError(billBroker, "SlippageTooHigh"); + }); + }); + + describe("successful deposit", function () { + it("should transfer usd from user", async function () { + const { billBroker, usd, perp, deployer } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("200")); + await billBroker.deposit( + usdFP("115"), + perpFP("200"), + usdFP("115"), + perpFP("200"), + ); + + await usd.approve(billBroker.target, usdFP("10")); + await expect(() => + billBroker.depositUSD(usdFP("10"), lpAmtFP("5"), lpAmtFP("15")), + ).to.changeTokenBalance(usd, deployer, usdFP("-10")); + }); + + it("should mint lp tokens", async function () { + const { billBroker, usd, perp, deployer } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("200")); + await billBroker.deposit( + usdFP("115"), + perpFP("200"), + usdFP("115"), + perpFP("200"), + ); + + await usd.approve(billBroker.target, usdFP("10")); + await expect(() => + billBroker.depositUSD(usdFP("10"), lpAmtFP("5"), lpAmtFP("15")), + ).to.changeTokenBalance( + billBroker, + deployer, + lpAmtFP("9.130434782608695652173913"), + ); + expect(await billBroker.totalSupply()).to.eq( + lpAmtFP("324.130434782608695652173913"), + ); + }); + + it("should return mint amount", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("200")); + await billBroker.deposit( + usdFP("115"), + perpFP("200"), + usdFP("115"), + perpFP("200"), + ); + + await usd.approve(billBroker.target, usdFP("10")); + const r = await billBroker.depositUSD.staticCall( + usdFP("10"), + lpAmtFP("5"), + lpAmtFP("15"), + ); + expect(r).to.eq(lpAmtFP("9.130434782608695652173913")); + }); + }); + + describe("when fee > 0", function () { + it("should withhold fees and mint lp tokens", async function () { + const { billBroker, usd, perp, deployer } = await loadFixture(setupContracts); + await billBroker.updateFees({ + mintFeePerc: percentageFP("0.1"), + burnFeePerc: 0n, + perpToUSDSwapFeePercs: { + lower: 0n, + upper: 0n, + }, + usdToPerpSwapFeePercs: { + lower: 0n, + upper: 0n, + }, + protocolSwapSharePerc: 0n, + }); + + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("200")); + await billBroker.deposit( + usdFP("115"), + perpFP("200"), + usdFP("115"), + perpFP("200"), + ); + + await usd.approve(billBroker.target, usdFP("10")); + await expect(() => + billBroker.depositUSD(usdFP("10"), lpAmtFP("5"), lpAmtFP("15")), + ).to.changeTokenBalance( + billBroker, + deployer, + lpAmtFP("7.395652173913043478260868"), + ); + }); + }); + }); + + describe("#depositPerp", function () { + describe("when paused", function () { + it("should revert", async function () { + const { billBroker } = await loadFixture(setupContracts); + await billBroker.pause(); + await expect(billBroker.depositPerp(perpFP("100"), lpAmtFP("50"), lpAmtFP("150"))) + .to.be.reverted; + }); + }); + + describe("when perpAmtIn is zero", function () { + it("should return zero", async function () { + const { billBroker } = await loadFixture(setupContracts); + const r = await billBroker.depositPerp.staticCall( + 0n, + lpAmtFP("50"), + lpAmtFP("150"), + ); + expect(r).to.eq(0n); + }); + }); + + describe("when slippage is too high", function () { + it("should revert if mintAmt is less than mintAmtMin", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("230")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("230"), + perpFP("100"), + usdFP("230"), + perpFP("100"), + ); + + await perp.approve(billBroker.target, perpFP("10")); + await expect( + billBroker.depositPerp(perpFP("10"), lpAmtFP("20"), lpAmtFP("30")), + ).to.be.revertedWithCustomError(billBroker, "SlippageTooHigh"); + }); + + it("should revert if mintAmt is greater than mintAmtMax", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("230")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("230"), + perpFP("100"), + usdFP("230"), + perpFP("100"), + ); + + await perp.approve(billBroker.target, perpFP("10")); + await expect( + billBroker.depositPerp(perpFP("10"), lpAmtFP("5"), lpAmtFP("9")), + ).to.be.revertedWithCustomError(billBroker, "SlippageTooHigh"); + }); + }); + + describe("successful deposit", function () { + it("should transfer perps from user", async function () { + const { billBroker, usd, perp, deployer } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("230")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("230"), + perpFP("100"), + usdFP("230"), + perpFP("100"), + ); + + await perp.approve(billBroker.target, perpFP("10")); + await expect(() => + billBroker.depositPerp(perpFP("10"), lpAmtFP("5"), lpAmtFP("15")), + ).to.changeTokenBalance(perp, deployer, perpFP("-10")); + }); + + it("should mint lp tokens", async function () { + const { billBroker, usd, perp, deployer } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("230")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("230"), + perpFP("100"), + usdFP("230"), + perpFP("100"), + ); + + await perp.approve(billBroker.target, perpFP("10")); + await expect(() => + billBroker.depositPerp(perpFP("10"), lpAmtFP("5"), lpAmtFP("15")), + ).to.changeTokenBalance(billBroker, deployer, lpAmtFP("11")); + expect(await billBroker.totalSupply()).to.eq(lpAmtFP("341")); + }); + + it("should return mint amount", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("230")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("230"), + perpFP("100"), + usdFP("230"), + perpFP("100"), + ); + + await perp.approve(billBroker.target, perpFP("10")); + const r = await billBroker.depositPerp.staticCall( + perpFP("10"), + lpAmtFP("5"), + lpAmtFP("15"), + ); + expect(r).to.eq(lpAmtFP("11")); + }); + }); + + describe("when fee > 0", function () { + it("should withhold fees and mint lp tokens", async function () { + const { billBroker, usd, perp, deployer } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("230")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("230"), + perpFP("100"), + usdFP("230"), + perpFP("100"), + ); + + await billBroker.updateFees({ + mintFeePerc: percentageFP("0.1"), + burnFeePerc: 0n, + perpToUSDSwapFeePercs: { + lower: 0n, + upper: 0n, + }, + usdToPerpSwapFeePercs: { + lower: 0n, + upper: 0n, + }, + protocolSwapSharePerc: 0n, + }); + await perp.approve(billBroker.target, perpFP("10")); + await expect(() => + billBroker.depositPerp(perpFP("10"), lpAmtFP("5"), lpAmtFP("15")), + ).to.changeTokenBalance(billBroker, deployer, lpAmtFP("9.9")); + }); + }); + }); + describe("#computeRedemptionAmts", function () { describe("when burn amount is zero", function () { it("should return zero", async function () { @@ -540,7 +1158,7 @@ describe("BillBroker", function () { }); }); - describe("when fee is zero", function () { + describe("when fee > 0", function () { it("should withhold and return redemption amounts", async function () { const { billBroker, usd, perp } = await loadFixture(setupContracts); await billBroker.updateFees({ From 94215cc9843ad2789a928a765b260f790901b8c5 Mon Sep 17 00:00:00 2001 From: Aalavandhan <6264334+aalavandhan@users.noreply.github.com> Date: Sat, 17 Aug 2024 10:18:44 -0400 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: Brandon Iles --- spot-vaults/contracts/BillBroker.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spot-vaults/contracts/BillBroker.sol b/spot-vaults/contracts/BillBroker.sol index cc299ab5..00095f59 100644 --- a/spot-vaults/contracts/BillBroker.sol +++ b/spot-vaults/contracts/BillBroker.sol @@ -602,7 +602,7 @@ contract BillBroker is // We only allow minting LP-tokens with USD, // when asset ratio is less than ONE; i.e) pool is overweight Perp. - if (assetRatioPre >= ONE || assetRatioPost >= ONE) { + if (assetRatioPre >= ONE || assetRatioPost > ONE) { return 0; } @@ -641,7 +641,7 @@ contract BillBroker is // We only allow minting LP-tokens with Perps, // when asset ratio is greater than ONE; i.e) pool is overweight USD. - if (assetRatioPre <= ONE || assetRatioPost <= ONE) { + if (assetRatioPre <= ONE || assetRatioPost < ONE) { return 0; } From daab9bd56c17b0f609d965bd410c75186d4ec6c1 Mon Sep 17 00:00:00 2001 From: aalavandhann <6264334+aalavandhan@users.noreply.github.com> Date: Sat, 17 Aug 2024 18:05:42 -0400 Subject: [PATCH 3/4] updated slippage condition --- spot-vaults/contracts/BillBroker.sol | 110 +++--- spot-vaults/test/BillBroker_deposit_redeem.ts | 373 +++++++----------- 2 files changed, 206 insertions(+), 277 deletions(-) diff --git a/spot-vaults/contracts/BillBroker.sol b/spot-vaults/contracts/BillBroker.sol index 00095f59..69207613 100644 --- a/spot-vaults/contracts/BillBroker.sol +++ b/spot-vaults/contracts/BillBroker.sol @@ -294,19 +294,33 @@ contract BillBroker is /// @notice Single sided usd token deposit and mint LP tokens. /// @param usdAmtIn The amount of usd tokens to be deposited. - /// @param mintAmtMin The minimum amount of LP tokens expected to be minted. - /// @param mintAmtMax The maximum amount of LP tokens expected to be minted. + /// @param postOpAssetRatioMax The system asset ratio can be no higher than this value after deposit. /// @return mintAmt The amount of LP tokens minted. function depositUSD( uint256 usdAmtIn, - uint256 mintAmtMin, - uint256 mintAmtMax + uint256 postOpAssetRatioMax ) external nonReentrant whenNotPaused returns (uint256 mintAmt) { - mintAmt = computeMintAmtWithUSD(usdAmtIn, reserveState()); + ReserveState memory s = reserveState(); + uint256 preOpAssetRatio = assetRatio(s); + uint256 postOpAssetRatio = assetRatio( + ReserveState({ + usdBalance: s.usdBalance + usdAmtIn, + perpBalance: s.perpBalance, + usdPrice: s.usdPrice, + perpPrice: s.perpPrice + }) + ); + + // We allow minting only pool is underweight usd + if (preOpAssetRatio >= ONE || postOpAssetRatio > ONE) { + return 0; + } + + mintAmt = computeMintAmtWithUSD(usdAmtIn, s); if (mintAmt <= 0) { return 0; } - if (mintAmt < mintAmtMin || mintAmt > mintAmtMax) { + if (postOpAssetRatio > postOpAssetRatioMax) { revert SlippageTooHigh(); } @@ -319,19 +333,33 @@ contract BillBroker is /// @notice Single sided perp token deposit and mint LP tokens. /// @param perpAmtIn The amount of perp tokens to be deposited. - /// @param mintAmtMin The minimum amount of LP tokens expected to be minted. - /// @param mintAmtMax The maximum amount of LP tokens expected to be minted. + /// @param postOpAssetRatioMin The system asset ratio can be no lower than this value after deposit. /// @return mintAmt The amount of LP tokens minted. function depositPerp( uint256 perpAmtIn, - uint256 mintAmtMin, - uint256 mintAmtMax + uint256 postOpAssetRatioMin ) external nonReentrant whenNotPaused returns (uint256 mintAmt) { - mintAmt = computeMintAmtWithPerp(perpAmtIn, reserveState()); + ReserveState memory s = reserveState(); + uint256 preOpAssetRatio = assetRatio(s); + uint256 postOpAssetRatio = assetRatio( + ReserveState({ + usdBalance: s.usdBalance, + perpBalance: s.perpBalance + perpAmtIn, + usdPrice: s.usdPrice, + perpPrice: s.perpPrice + }) + ); + + // We allow minting only pool is underweight perp + if (preOpAssetRatio <= ONE || postOpAssetRatio < ONE) { + return 0; + } + + mintAmt = computeMintAmtWithPerp(perpAmtIn, s); if (mintAmt <= 0) { return 0; } - if (mintAmt < mintAmtMin || mintAmt > mintAmtMax) { + if (postOpAssetRatio < postOpAssetRatioMin) { revert SlippageTooHigh(); } @@ -585,35 +613,22 @@ contract BillBroker is function computeMintAmtWithUSD( uint256 usdAmtIn, ReserveState memory s - ) public view returns (uint256 mintAmt) { + ) public view returns (uint256) { if (usdAmtIn <= 0) { return 0; } - uint256 assetRatioPre = assetRatio(s); - uint256 assetRatioPost = assetRatio( - ReserveState({ - usdBalance: s.usdBalance + usdAmtIn, - perpBalance: s.perpBalance, - usdPrice: s.usdPrice, - perpPrice: s.perpPrice - }) - ); - - // We only allow minting LP-tokens with USD, - // when asset ratio is less than ONE; i.e) pool is overweight Perp. - if (assetRatioPre >= ONE || assetRatioPost > ONE) { - return 0; - } - uint256 valueIn = s.usdPrice.mulDiv(usdAmtIn, usdUnitAmt); uint256 totalReserveVal = (s.usdPrice.mulDiv(s.usdBalance, usdUnitAmt) + s.perpPrice.mulDiv(s.perpBalance, perpUnitAmt)); - mintAmt = valueIn.mulDiv(totalSupply(), totalReserveVal).mulDiv( - ONE - fees.mintFeePerc, - ONE - ); + return + (totalReserveVal > 0) + ? valueIn.mulDiv(totalSupply(), totalReserveVal).mulDiv( + ONE - fees.mintFeePerc, + ONE + ) + : 0; } /// @notice Computes the amount of LP tokens minted, @@ -624,35 +639,22 @@ contract BillBroker is function computeMintAmtWithPerp( uint256 perpAmtIn, ReserveState memory s - ) public view returns (uint256 mintAmt) { + ) public view returns (uint256) { if (perpAmtIn <= 0) { return 0; } - uint256 assetRatioPre = assetRatio(s); - uint256 assetRatioPost = assetRatio( - ReserveState({ - usdBalance: s.usdBalance, - perpBalance: s.perpBalance + perpAmtIn, - usdPrice: s.usdPrice, - perpPrice: s.perpPrice - }) - ); - - // We only allow minting LP-tokens with Perps, - // when asset ratio is greater than ONE; i.e) pool is overweight USD. - if (assetRatioPre <= ONE || assetRatioPost < ONE) { - return 0; - } - uint256 valueIn = s.perpPrice.mulDiv(perpAmtIn, perpUnitAmt); uint256 totalReserveVal = (s.usdPrice.mulDiv(s.usdBalance, usdUnitAmt) + s.perpPrice.mulDiv(s.perpBalance, perpUnitAmt)); - mintAmt = valueIn.mulDiv(totalSupply(), totalReserveVal).mulDiv( - ONE - fees.mintFeePerc, - ONE - ); + return + (totalReserveVal > 0) + ? valueIn.mulDiv(totalSupply(), totalReserveVal).mulDiv( + ONE - fees.mintFeePerc, + ONE + ) + : 0; } /// @notice Computes the amount of usd and perp tokens redeemed, diff --git a/spot-vaults/test/BillBroker_deposit_redeem.ts b/spot-vaults/test/BillBroker_deposit_redeem.ts index d8bcc630..62c9199b 100644 --- a/spot-vaults/test/BillBroker_deposit_redeem.ts +++ b/spot-vaults/test/BillBroker_deposit_redeem.ts @@ -48,6 +48,16 @@ describe("BillBroker", function () { return { deployer, otherUser, usd, perp, pricingStrategy, billBroker }; } + async function assetRatio(billBroker) { + const r = await billBroker.reserveState.staticCall(); + return billBroker.assetRatio({ + usdBalance: r[0], + perpBalance: r[1], + usdPrice: r[2], + perpPrice: r[3], + }); + } + describe("#computeMintAmt", function () { describe("when amounts available are zero", function () { it("should return zero", async function () { @@ -170,15 +180,7 @@ describe("BillBroker", function () { await billBroker.swapUSDForPerps(usdFP("115"), 0n); expect(await perp.balanceOf(billBroker.target)).to.eq(0n); - const s = await billBroker.reserveState.staticCall(); - expect( - await billBroker.assetRatio({ - usdBalance: s[0], - perpBalance: s[1], - usdPrice: s[2], - perpPrice: s[3], - }), - ).to.eq(ethers.MaxUint256); + expect(await assetRatio(billBroker)).to.eq(ethers.MaxUint256); const r = await billBroker.computeMintAmt.staticCall(usdFP("100"), 0n); expect(r[0]).to.eq(lpAmtFP("93.478260869565217391304347")); @@ -203,15 +205,7 @@ describe("BillBroker", function () { await billBroker.swapPerpsForUSD(perpFP("100"), 0n); expect(await usd.balanceOf(billBroker.target)).to.eq(0n); - const s = await billBroker.reserveState.staticCall(); - expect( - await billBroker.assetRatio({ - usdBalance: s[0], - perpBalance: s[1], - usdPrice: s[2], - perpPrice: s[3], - }), - ).to.eq(0); + expect(await assetRatio(billBroker)).to.eq(0); const r = await billBroker.computeMintAmt.staticCall(0n, perpFP("100")); expect(r[0]).to.eq(lpAmtFP("107.5")); @@ -236,67 +230,7 @@ describe("BillBroker", function () { }); }); - describe("when assetRatioPre > 1", function () { - it("should return zero", async function () { - const { billBroker, usd, perp } = await loadFixture(setupContracts); - await usd.approve(billBroker.target, usdFP("230")); - await perp.approve(billBroker.target, perpFP("100")); - await billBroker.deposit( - usdFP("115"), - perpFP("100"), - usdFP("115"), - perpFP("100"), - ); - expect(await billBroker.computeMintAmtWithUSD.staticCall(usdFP("100"))).to.eq(0n); - }); - }); - - describe("when assetRatioPost > 1", function () { - it("should return zero", async function () { - const { billBroker, usd, perp } = await loadFixture(setupContracts); - await usd.approve(billBroker.target, usdFP("100")); - await perp.approve(billBroker.target, perpFP("100")); - await billBroker.deposit( - usdFP("100"), - perpFP("100"), - usdFP("100"), - perpFP("100"), - ); - expect(await billBroker.computeMintAmtWithUSD.staticCall(usdFP("100"))).to.eq(0n); - }); - }); - - describe("when assetRatioPre = 1", function () { - it("should return the mint amount", async function () { - const { billBroker, usd, perp } = await loadFixture(setupContracts); - await usd.approve(billBroker.target, usdFP("115")); - await perp.approve(billBroker.target, perpFP("100")); - await billBroker.deposit( - usdFP("115"), - perpFP("100"), - usdFP("115"), - perpFP("100"), - ); - expect(await billBroker.computeMintAmtWithUSD.staticCall(usdFP("1"))).to.eq(0n); - }); - }); - - describe("when assetRatioPost = 1", function () { - it("should return the mint amount", async function () { - const { billBroker, usd, perp } = await loadFixture(setupContracts); - await usd.approve(billBroker.target, usdFP("115")); - await perp.approve(billBroker.target, perpFP("200")); - await billBroker.deposit( - usdFP("115"), - perpFP("200"), - usdFP("115"), - perpFP("200"), - ); - expect(await billBroker.computeMintAmtWithUSD.staticCall(usdFP("115"))).to.eq(0n); - }); - }); - - describe("when assetRatioPost < 1", function () { + describe("when fee = 0", function () { it("should return the mint amount", async function () { const { billBroker, usd, perp } = await loadFixture(setupContracts); await usd.approve(billBroker.target, usdFP("115")); @@ -359,15 +293,7 @@ describe("BillBroker", function () { await billBroker.swapPerpsForUSD(perpFP("100"), 0n); expect(await usd.balanceOf(billBroker.target)).to.eq(0n); - const s = await billBroker.reserveState.staticCall(); - expect( - await billBroker.assetRatio({ - usdBalance: s[0], - perpBalance: s[1], - usdPrice: s[2], - perpPrice: s[3], - }), - ).to.eq(0); + expect(await assetRatio(billBroker)).to.eq(0); expect(await billBroker.computeMintAmtWithUSD.staticCall(usdFP("115"))).to.eq( lpAmtFP("107.5"), ); @@ -392,68 +318,7 @@ describe("BillBroker", function () { }); }); - describe("when assetRatioPre < 1", function () { - it("should return zero", async function () { - const { billBroker, usd, perp } = await loadFixture(setupContracts); - await usd.approve(billBroker.target, usdFP("200")); - await perp.approve(billBroker.target, perpFP("100")); - await billBroker.deposit( - usdFP("200"), - perpFP("100"), - usdFP("200"), - perpFP("100"), - ); - expect(await billBroker.computeMintAmtWithPerp.staticCall(perpFP("100"))).to.eq( - 0n, - ); - }); - }); - - describe("when assetRatioPost < 1", function () { - it("should return zero", async function () { - const { billBroker, usd, perp } = await loadFixture(setupContracts); - await usd.approve(billBroker.target, usdFP("120")); - await perp.approve(billBroker.target, perpFP("100")); - await billBroker.deposit( - usdFP("120"), - perpFP("100"), - usdFP("120"), - perpFP("100"), - ); - expect(await billBroker.computeMintAmtWithPerp.staticCall(perpFP("100"))).to.eq( - 0n, - ); - }); - }); - - describe("when assetRatioPre = 1", function () { - it("should return the mint amount", async function () { - const { billBroker, usd, perp } = await loadFixture(setupContracts); - await usd.approve(billBroker.target, usdFP("115")); - await perp.approve(billBroker.target, perpFP("100")); - await billBroker.deposit( - usdFP("115"), - perpFP("100"), - usdFP("115"), - perpFP("100"), - ); - expect(await billBroker.computeMintAmtWithPerp.staticCall(perpFP("1"))).to.eq(0n); - }); - }); - - describe("when assetRatioPost = 1", function () { - it("should return the mint amount", async function () { - const { billBroker, usd, perp } = await loadFixture(setupContracts); - await usd.approve(billBroker.target, usdFP("115")); - await perp.approve(billBroker.target, perpFP("90")); - await billBroker.deposit(usdFP("115"), perpFP("90"), usdFP("115"), perpFP("90")); - expect(await billBroker.computeMintAmtWithPerp.staticCall(perpFP("10"))).to.eq( - 0n, - ); - }); - }); - - describe("when assetRatioPost > 1", function () { + describe("when fee = 0", function () { it("should return the mint amount", async function () { const { billBroker, usd, perp } = await loadFixture(setupContracts); await usd.approve(billBroker.target, usdFP("200")); @@ -516,15 +381,7 @@ describe("BillBroker", function () { await billBroker.swapUSDForPerps(usdFP("115"), 0n); expect(await perp.balanceOf(billBroker.target)).to.eq(0n); - const s = await billBroker.reserveState.staticCall(); - expect( - await billBroker.assetRatio({ - usdBalance: s[0], - perpBalance: s[1], - usdPrice: s[2], - perpPrice: s[3], - }), - ).to.eq(ethers.MaxUint256); + expect(await assetRatio(billBroker)).to.eq(ethers.MaxUint256); expect(await billBroker.computeMintAmtWithPerp.staticCall(perpFP("100"))).to.eq( lpAmtFP("107.5"), ); @@ -744,15 +601,7 @@ describe("BillBroker", function () { await billBroker.swapUSDForPerps(usdFP("115"), 0n); expect(await perp.balanceOf(billBroker.target)).to.eq(0n); - const s = await billBroker.reserveState.staticCall(); - expect( - await billBroker.assetRatio({ - usdBalance: s[0], - perpBalance: s[1], - usdPrice: s[2], - perpPrice: s[3], - }), - ).to.eq(ethers.MaxUint256); + expect(await assetRatio(billBroker)).to.eq(ethers.MaxUint256); await usd.approve(billBroker.target, usdFP("115")); await expect(() => @@ -781,15 +630,7 @@ describe("BillBroker", function () { await billBroker.swapPerpsForUSD(perpFP("100"), 0n); expect(await usd.balanceOf(billBroker.target)).to.eq(0n); - const s = await billBroker.reserveState.staticCall(); - expect( - await billBroker.assetRatio({ - usdBalance: s[0], - perpBalance: s[1], - usdPrice: s[2], - perpPrice: s[3], - }), - ).to.eq(0); + expect(await assetRatio(billBroker)).to.eq(0); await perp.approve(billBroker.target, perpFP("100")); await expect(() => @@ -804,25 +645,75 @@ describe("BillBroker", function () { it("should revert", async function () { const { billBroker } = await loadFixture(setupContracts); await billBroker.pause(); - await expect(billBroker.depositUSD(usdFP("115"), lpAmtFP("100"), lpAmtFP("200"))) - .to.be.reverted; + await expect(billBroker.depositUSD(usdFP("115"), percentageFP("1"))).to.be + .reverted; }); }); describe("when usdAmtIn is zero", function () { it("should return zero", async function () { const { billBroker } = await loadFixture(setupContracts); - const r = await billBroker.depositUSD.staticCall( - 0n, - lpAmtFP("100"), - lpAmtFP("200"), - ); + const r = await billBroker.depositUSD.staticCall(0n, percentageFP("1")); expect(r).to.eq(0n); }); }); - describe("when slippage is too high", function () { - it("should revert if mintAmt is less than mintAmtMin", async function () { + describe("when assetRatioPre > 1", function () { + it("should return zero", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("230")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("115"), + perpFP("100"), + usdFP("115"), + perpFP("100"), + ); + await usd.approve(billBroker.target, usdFP("100")); + expect( + await billBroker.depositUSD.staticCall(usdFP("100"), ethers.MaxUint256), + ).to.eq(0n); + }); + }); + + describe("when assetRatioPost > 1", function () { + it("should return zero", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("100")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("100"), + perpFP("100"), + usdFP("100"), + perpFP("100"), + ); + await usd.approve(billBroker.target, usdFP("100")); + expect( + await billBroker.depositUSD.staticCall(usdFP("100"), ethers.MaxUint256), + ).to.eq(0n); + }); + }); + + describe("when assetRatioPre = 1", function () { + it("should return the mint amount", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("115"), + perpFP("100"), + usdFP("115"), + perpFP("100"), + ); + await usd.approve(billBroker.target, usdFP("1")); + expect( + await billBroker.depositUSD.staticCall(usdFP("1"), ethers.MaxUint256), + ).to.eq(0n); + }); + }); + + describe("when assetRatioPost = 1", function () { + it("should return the mint amount", async function () { const { billBroker, usd, perp } = await loadFixture(setupContracts); await usd.approve(billBroker.target, usdFP("115")); await perp.approve(billBroker.target, perpFP("200")); @@ -832,14 +723,15 @@ describe("BillBroker", function () { usdFP("115"), perpFP("200"), ); - - await usd.approve(billBroker.target, usdFP("10")); - await expect( - billBroker.depositUSD(usdFP("10"), lpAmtFP("20"), lpAmtFP("30")), - ).to.be.revertedWithCustomError(billBroker, "SlippageTooHigh"); + await usd.approve(billBroker.target, usdFP("115")); + expect( + await billBroker.depositUSD.staticCall(usdFP("115"), ethers.MaxUint256), + ).to.eq(lpAmtFP("105")); }); + }); - it("should revert if mintAmt is greater than mintAmtMax", async function () { + describe("when slippage is too high", function () { + it("should revert asset ratio increases beyond limit", async function () { const { billBroker, usd, perp } = await loadFixture(setupContracts); await usd.approve(billBroker.target, usdFP("115")); await perp.approve(billBroker.target, perpFP("200")); @@ -852,7 +744,7 @@ describe("BillBroker", function () { await usd.approve(billBroker.target, usdFP("10")); await expect( - billBroker.depositUSD(usdFP("10"), lpAmtFP("10"), lpAmtFP("20")), + billBroker.depositUSD(usdFP("10"), percentageFP("0.50")), ).to.be.revertedWithCustomError(billBroker, "SlippageTooHigh"); }); }); @@ -871,7 +763,7 @@ describe("BillBroker", function () { await usd.approve(billBroker.target, usdFP("10")); await expect(() => - billBroker.depositUSD(usdFP("10"), lpAmtFP("5"), lpAmtFP("15")), + billBroker.depositUSD(usdFP("10"), percentageFP("1")), ).to.changeTokenBalance(usd, deployer, usdFP("-10")); }); @@ -888,7 +780,7 @@ describe("BillBroker", function () { await usd.approve(billBroker.target, usdFP("10")); await expect(() => - billBroker.depositUSD(usdFP("10"), lpAmtFP("5"), lpAmtFP("15")), + billBroker.depositUSD(usdFP("10"), percentageFP("1")), ).to.changeTokenBalance( billBroker, deployer, @@ -911,11 +803,7 @@ describe("BillBroker", function () { ); await usd.approve(billBroker.target, usdFP("10")); - const r = await billBroker.depositUSD.staticCall( - usdFP("10"), - lpAmtFP("5"), - lpAmtFP("15"), - ); + const r = await billBroker.depositUSD.staticCall(usdFP("10"), percentageFP("1")); expect(r).to.eq(lpAmtFP("9.130434782608695652173913")); }); }); @@ -948,7 +836,7 @@ describe("BillBroker", function () { await usd.approve(billBroker.target, usdFP("10")); await expect(() => - billBroker.depositUSD(usdFP("10"), lpAmtFP("5"), lpAmtFP("15")), + billBroker.depositUSD(usdFP("10"), percentageFP("1")), ).to.changeTokenBalance( billBroker, deployer, @@ -963,42 +851,82 @@ describe("BillBroker", function () { it("should revert", async function () { const { billBroker } = await loadFixture(setupContracts); await billBroker.pause(); - await expect(billBroker.depositPerp(perpFP("100"), lpAmtFP("50"), lpAmtFP("150"))) - .to.be.reverted; + await expect(billBroker.depositPerp(perpFP("100"), percentageFP("1"))).to.be + .reverted; }); }); describe("when perpAmtIn is zero", function () { it("should return zero", async function () { const { billBroker } = await loadFixture(setupContracts); - const r = await billBroker.depositPerp.staticCall( - 0n, - lpAmtFP("50"), - lpAmtFP("150"), - ); + const r = await billBroker.depositPerp.staticCall(0n, percentageFP("1")); expect(r).to.eq(0n); }); }); - describe("when slippage is too high", function () { - it("should revert if mintAmt is less than mintAmtMin", async function () { + describe("when assetRatioPre < 1", function () { + it("should return zero", async function () { const { billBroker, usd, perp } = await loadFixture(setupContracts); - await usd.approve(billBroker.target, usdFP("230")); + await usd.approve(billBroker.target, usdFP("200")); await perp.approve(billBroker.target, perpFP("100")); await billBroker.deposit( - usdFP("230"), + usdFP("200"), perpFP("100"), - usdFP("230"), + usdFP("200"), perpFP("100"), ); + await perp.approve(billBroker.target, perpFP("100")); + expect(await billBroker.depositPerp.staticCall(perpFP("100"), 0n)).to.eq(0n); + }); + }); + describe("when assetRatioPost < 1", function () { + it("should return zero", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("120")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("120"), + perpFP("100"), + usdFP("120"), + perpFP("100"), + ); + await perp.approve(billBroker.target, perpFP("100")); + expect(await billBroker.depositPerp.staticCall(perpFP("100"), 0n)).to.eq(0n); + }); + }); + + describe("when assetRatioPre = 1", function () { + it("should return the mint amount", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("115"), + perpFP("100"), + usdFP("115"), + perpFP("100"), + ); + await perp.approve(billBroker.target, perpFP("1")); + expect(await billBroker.depositPerp.staticCall(perpFP("1"), 0n)).to.eq(0n); + }); + }); + + describe("when assetRatioPost = 1", function () { + it("should return the mint amount", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("90")); + await billBroker.deposit(usdFP("115"), perpFP("90"), usdFP("115"), perpFP("90")); await perp.approve(billBroker.target, perpFP("10")); - await expect( - billBroker.depositPerp(perpFP("10"), lpAmtFP("20"), lpAmtFP("30")), - ).to.be.revertedWithCustomError(billBroker, "SlippageTooHigh"); + expect(await billBroker.depositPerp.staticCall(perpFP("10"), 0n)).to.eq( + lpAmtFP("10.789473684210526315789473"), + ); }); + }); - it("should revert if mintAmt is greater than mintAmtMax", async function () { + describe("when slippage is too high", function () { + it("should revert asset ratio reduces below the limit", async function () { const { billBroker, usd, perp } = await loadFixture(setupContracts); await usd.approve(billBroker.target, usdFP("230")); await perp.approve(billBroker.target, perpFP("100")); @@ -1011,7 +939,7 @@ describe("BillBroker", function () { await perp.approve(billBroker.target, perpFP("10")); await expect( - billBroker.depositPerp(perpFP("10"), lpAmtFP("5"), lpAmtFP("9")), + billBroker.depositPerp(perpFP("10"), percentageFP("1.85")), ).to.be.revertedWithCustomError(billBroker, "SlippageTooHigh"); }); }); @@ -1030,7 +958,7 @@ describe("BillBroker", function () { await perp.approve(billBroker.target, perpFP("10")); await expect(() => - billBroker.depositPerp(perpFP("10"), lpAmtFP("5"), lpAmtFP("15")), + billBroker.depositPerp(perpFP("10"), percentageFP("1")), ).to.changeTokenBalance(perp, deployer, perpFP("-10")); }); @@ -1047,7 +975,7 @@ describe("BillBroker", function () { await perp.approve(billBroker.target, perpFP("10")); await expect(() => - billBroker.depositPerp(perpFP("10"), lpAmtFP("5"), lpAmtFP("15")), + billBroker.depositPerp(perpFP("10"), percentageFP("1")), ).to.changeTokenBalance(billBroker, deployer, lpAmtFP("11")); expect(await billBroker.totalSupply()).to.eq(lpAmtFP("341")); }); @@ -1066,8 +994,7 @@ describe("BillBroker", function () { await perp.approve(billBroker.target, perpFP("10")); const r = await billBroker.depositPerp.staticCall( perpFP("10"), - lpAmtFP("5"), - lpAmtFP("15"), + percentageFP("1"), ); expect(r).to.eq(lpAmtFP("11")); }); @@ -1100,7 +1027,7 @@ describe("BillBroker", function () { }); await perp.approve(billBroker.target, perpFP("10")); await expect(() => - billBroker.depositPerp(perpFP("10"), lpAmtFP("5"), lpAmtFP("15")), + billBroker.depositPerp(perpFP("10"), percentageFP("1")), ).to.changeTokenBalance(billBroker, deployer, lpAmtFP("9.9")); }); }); From aa698d311418e091221c18e3e1b0d6630a504740 Mon Sep 17 00:00:00 2001 From: aalavandhann <6264334+aalavandhan@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:23:05 -0400 Subject: [PATCH 4/4] Added events to keep track of swaps offchain --- spot-vaults/contracts/BillBroker.sol | 72 ++++++++++---- spot-vaults/hardhat.config.ts | 1 + spot-vaults/package.json | 2 +- spot-vaults/test/BillBroker_deposit_redeem.ts | 39 ++++++++ spot-vaults/test/BillBroker_swap.ts | 96 +++++++++++++++++++ yarn.lock | 90 ++++++++--------- 6 files changed, 235 insertions(+), 65 deletions(-) diff --git a/spot-vaults/contracts/BillBroker.sol b/spot-vaults/contracts/BillBroker.sol index 69207613..0e12f3b4 100644 --- a/spot-vaults/contracts/BillBroker.sol +++ b/spot-vaults/contracts/BillBroker.sol @@ -115,6 +115,29 @@ contract BillBroker is /// the swap fees transition from a flat percentage fee to a linear function. Range public arSoftBound; + //-------------------------------------------------------------------------- + // Events + + /// @notice Emitted when a user deposits USD tokens to mint LP tokens. + /// @param usdAmtIn The amount of USD tokens deposited. + /// @param preOpState The reserve state before the deposit operation. + event DepositUSD(uint256 usdAmtIn, ReserveState preOpState); + + /// @notice Emitted when a user deposits Perp tokens to mint LP tokens. + /// @param perpAmtIn The amount of Perp tokens deposited. + /// @param preOpState The reserve state before the deposit operation. + event DepositPerp(uint256 perpAmtIn, ReserveState preOpState); + + /// @notice Emitted when a user swaps Perp tokens for USD tokens. + /// @param perpAmtIn The amount of Perp tokens swapped in. + /// @param preOpState The reserve state before the swap operation. + event SwapPerpsForUSD(uint256 perpAmtIn, ReserveState preOpState); + + /// @notice Emitted when a user swaps USD tokens for Perp tokens. + /// @param usdAmtIn The amount of USD tokens swapped in. + /// @param preOpState The reserve state before the swap operation. + event SwapUSDForPerps(uint256 usdAmtIn, ReserveState preOpState); + //-------------------------------------------------------------------------- // Modifiers @@ -300,14 +323,14 @@ contract BillBroker is uint256 usdAmtIn, uint256 postOpAssetRatioMax ) external nonReentrant whenNotPaused returns (uint256 mintAmt) { - ReserveState memory s = reserveState(); - uint256 preOpAssetRatio = assetRatio(s); + ReserveState memory preOpState = reserveState(); + uint256 preOpAssetRatio = assetRatio(preOpState); uint256 postOpAssetRatio = assetRatio( ReserveState({ - usdBalance: s.usdBalance + usdAmtIn, - perpBalance: s.perpBalance, - usdPrice: s.usdPrice, - perpPrice: s.perpPrice + usdBalance: preOpState.usdBalance + usdAmtIn, + perpBalance: preOpState.perpBalance, + usdPrice: preOpState.usdPrice, + perpPrice: preOpState.perpPrice }) ); @@ -316,7 +339,7 @@ contract BillBroker is return 0; } - mintAmt = computeMintAmtWithUSD(usdAmtIn, s); + mintAmt = computeMintAmtWithUSD(usdAmtIn, preOpState); if (mintAmt <= 0) { return 0; } @@ -329,6 +352,9 @@ contract BillBroker is // mint LP tokens to the user _mint(msg.sender, mintAmt); + + // Emit deposit info + emit DepositUSD(usdAmtIn, preOpState); } /// @notice Single sided perp token deposit and mint LP tokens. @@ -339,14 +365,14 @@ contract BillBroker is uint256 perpAmtIn, uint256 postOpAssetRatioMin ) external nonReentrant whenNotPaused returns (uint256 mintAmt) { - ReserveState memory s = reserveState(); - uint256 preOpAssetRatio = assetRatio(s); + ReserveState memory preOpState = reserveState(); + uint256 preOpAssetRatio = assetRatio(preOpState); uint256 postOpAssetRatio = assetRatio( ReserveState({ - usdBalance: s.usdBalance, - perpBalance: s.perpBalance + perpAmtIn, - usdPrice: s.usdPrice, - perpPrice: s.perpPrice + usdBalance: preOpState.usdBalance, + perpBalance: preOpState.perpBalance + perpAmtIn, + usdPrice: preOpState.usdPrice, + perpPrice: preOpState.perpPrice }) ); @@ -355,7 +381,7 @@ contract BillBroker is return 0; } - mintAmt = computeMintAmtWithPerp(perpAmtIn, s); + mintAmt = computeMintAmtWithPerp(perpAmtIn, preOpState); if (mintAmt <= 0) { return 0; } @@ -368,6 +394,9 @@ contract BillBroker is // mint LP tokens to the user _mint(msg.sender, mintAmt); + + // Emit deposit info + emit DepositPerp(perpAmtIn, preOpState); } /// @notice Burns LP tokens and redeems usd and perp tokens. @@ -404,10 +433,11 @@ contract BillBroker is uint256 perpAmtMin ) external nonReentrant whenNotPaused returns (uint256 perpAmtOut) { // compute perp amount out + ReserveState memory preOpState = reserveState(); uint256 protocolFeePerpAmt; (perpAmtOut, , protocolFeePerpAmt) = computeUSDToPerpSwapAmt( usdAmtIn, - reserveState() + preOpState ); if (usdAmtIn <= 0 || perpAmtOut <= 0) { revert UnacceptableSwap(); @@ -426,6 +456,9 @@ contract BillBroker is // transfer perps out to the user perp.safeTransfer(msg.sender, perpAmtOut); + + // Emit swap info + emit SwapUSDForPerps(usdAmtIn, preOpState); } /// @notice Swaps perp tokens from the user for usd tokens from the reserve. @@ -437,11 +470,9 @@ contract BillBroker is uint256 usdAmtMin ) external nonReentrant whenNotPaused returns (uint256 usdAmtOut) { // Compute swap amount + ReserveState memory preOpState = reserveState(); uint256 protocolFeeUsdAmt; - (usdAmtOut, , protocolFeeUsdAmt) = computePerpToUSDSwapAmt( - perpAmtIn, - reserveState() - ); + (usdAmtOut, , protocolFeeUsdAmt) = computePerpToUSDSwapAmt(perpAmtIn, preOpState); if (perpAmtIn <= 0 || usdAmtOut <= 0) { revert UnacceptableSwap(); } @@ -459,6 +490,9 @@ contract BillBroker is // transfer usd out to the user usd.safeTransfer(msg.sender, usdAmtOut); + + // Emit swap info + emit SwapPerpsForUSD(perpAmtIn, preOpState); } //----------------------------------------------------------------------------- diff --git a/spot-vaults/hardhat.config.ts b/spot-vaults/hardhat.config.ts index 88c2c00e..a9ad1cfa 100644 --- a/spot-vaults/hardhat.config.ts +++ b/spot-vaults/hardhat.config.ts @@ -78,6 +78,7 @@ export default { enabled: !!process.env.REPORT_GAS, excludeContracts: ["_test/"], coinmarketcap: process.env.COINMARKETCAP_API_KEY, + L1Etherscan: process.env.ETHERSCAN_API_KEY, }, etherscan: { apiKey: process.env.ETHERSCAN_API_KEY, diff --git a/spot-vaults/package.json b/spot-vaults/package.json index 311a8cd2..b2915325 100644 --- a/spot-vaults/package.json +++ b/spot-vaults/package.json @@ -62,7 +62,7 @@ "ethers": "^6.6.0", "ethers-v5": "npm:ethers@^5.7.0", "ganache-cli": "latest", - "hardhat": "^2.22.6", + "hardhat": "^2.22.8", "hardhat-gas-reporter": "latest", "lodash": "^4.17.21", "prettier": "^2.7.1", diff --git a/spot-vaults/test/BillBroker_deposit_redeem.ts b/spot-vaults/test/BillBroker_deposit_redeem.ts index 62c9199b..784f65a9 100644 --- a/spot-vaults/test/BillBroker_deposit_redeem.ts +++ b/spot-vaults/test/BillBroker_deposit_redeem.ts @@ -791,6 +791,26 @@ describe("BillBroker", function () { ); }); + it("should emit DepositUSD", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("115")); + await perp.approve(billBroker.target, perpFP("200")); + await billBroker.deposit( + usdFP("115"), + perpFP("200"), + usdFP("115"), + perpFP("200"), + ); + await usd.approve(billBroker.target, usdFP("10")); + const r = await billBroker.reserveState.staticCall(); + await expect(billBroker.depositUSD(usdFP("10"), percentageFP("1"))) + .to.emit(billBroker, "DepositUSD") + .withArgs(usdFP("10"), r); + expect(await billBroker.totalSupply()).to.eq( + lpAmtFP("324.130434782608695652173913"), + ); + }); + it("should return mint amount", async function () { const { billBroker, usd, perp } = await loadFixture(setupContracts); await usd.approve(billBroker.target, usdFP("115")); @@ -980,6 +1000,25 @@ describe("BillBroker", function () { expect(await billBroker.totalSupply()).to.eq(lpAmtFP("341")); }); + it("should emit DepositPerp", async function () { + const { billBroker, usd, perp } = await loadFixture(setupContracts); + await usd.approve(billBroker.target, usdFP("230")); + await perp.approve(billBroker.target, perpFP("100")); + await billBroker.deposit( + usdFP("230"), + perpFP("100"), + usdFP("230"), + perpFP("100"), + ); + + await perp.approve(billBroker.target, perpFP("10")); + const r = await billBroker.reserveState.staticCall(); + await expect(billBroker.depositPerp(perpFP("10"), percentageFP("1"))) + .to.emit(billBroker, "DepositPerp") + .withArgs(perpFP("10"), r); + expect(await billBroker.totalSupply()).to.eq(lpAmtFP("341")); + }); + it("should return mint amount", async function () { const { billBroker, usd, perp } = await loadFixture(setupContracts); await usd.approve(billBroker.target, usdFP("230")); diff --git a/spot-vaults/test/BillBroker_swap.ts b/spot-vaults/test/BillBroker_swap.ts index c58faab5..d22bca2d 100644 --- a/spot-vaults/test/BillBroker_swap.ts +++ b/spot-vaults/test/BillBroker_swap.ts @@ -570,6 +570,14 @@ describe("BillBroker", function () { expect(r.usdBalance).to.eq(usdFP("115115")); expect(r.perpBalance).to.eq(perpFP("99900")); }); + + it("should emit SwapUSDForPerps", async function () { + const { billBroker } = await loadFixture(setupContracts); + const r = await billBroker.reserveState.staticCall(); + await expect(billBroker.swapUSDForPerps(usdFP("115"), perpFP("100"))) + .to.emit(billBroker, "SwapUSDForPerps") + .withArgs(usdFP("115"), r); + }); }); describe("stable swap with fees", function () { @@ -622,6 +630,19 @@ describe("BillBroker", function () { expect(r.usdBalance).to.eq(usdFP("115115")); expect(r.perpBalance).to.eq(perpFP("99905")); }); + it("should emit SwapUSDForPerps", async function () { + const { billBroker } = await loadFixture(setupContracts); + await updateFees(billBroker, { + usdToPerpSwapFeePercs: { + lower: percentageFP("0.05"), + upper: percentageFP("0.5"), + }, + }); + const r = await billBroker.reserveState.staticCall(); + await expect(billBroker.swapUSDForPerps(usdFP("115"), perpFP("95"))) + .to.emit(billBroker, "SwapUSDForPerps") + .withArgs(usdFP("115"), r); + }); }); describe("stable swap with protocol fees", function () { @@ -697,6 +718,20 @@ describe("BillBroker", function () { expect(r.usdBalance).to.eq(usdFP("115115")); expect(r.perpBalance).to.eq(perpFP("99904.5")); }); + it("should emit SwapUSDForPerps", async function () { + const { billBroker } = await loadFixture(setupContracts); + await updateFees(billBroker, { + usdToPerpSwapFeePercs: { + lower: percentageFP("0.05"), + upper: percentageFP("0.5"), + }, + protocolSwapSharePerc: percentageFP("0.1"), + }); + const r = await billBroker.reserveState.staticCall(); + await expect(billBroker.swapUSDForPerps(usdFP("115"), perpFP("95"))) + .to.emit(billBroker, "SwapUSDForPerps") + .withArgs(usdFP("115"), r); + }); }); describe("when swap amount pushes system outside soft bound", async function () { @@ -750,6 +785,19 @@ describe("BillBroker", function () { expect(r.usdBalance).to.eq(usdFP("118795")); expect(r.perpBalance).to.eq(perpFP("96865")); }); + it("should emit SwapUSDForPerps", async function () { + const { billBroker } = await loadFixture(setupContracts); + await updateFees(billBroker, { + usdToPerpSwapFeePercs: { + lower: percentageFP("0.05"), + upper: percentageFP("0.5"), + }, + }); + const r = await billBroker.reserveState.staticCall(); + await expect(billBroker.swapUSDForPerps(usdFP("3795"), perpFP("3130"))) + .to.emit(billBroker, "SwapUSDForPerps") + .withArgs(usdFP("3795"), r); + }); }); describe("when swap amount pushes system outside hard bound", async function () { @@ -873,6 +921,13 @@ describe("BillBroker", function () { expect(r.usdBalance).to.eq(usdFP("114885")); expect(r.perpBalance).to.eq(perpFP("100100")); }); + it("should emit SwapPerpsForUSD", async function () { + const { billBroker } = await loadFixture(setupContracts); + const r = await billBroker.reserveState.staticCall(); + await expect(billBroker.swapPerpsForUSD(perpFP("100"), usdFP("115"))) + .to.emit(billBroker, "SwapPerpsForUSD") + .withArgs(perpFP("100"), r); + }); }); describe("stable swap with fees", function () { @@ -925,6 +980,19 @@ describe("BillBroker", function () { expect(r.usdBalance).to.eq(usdFP("114896.5")); expect(r.perpBalance).to.eq(perpFP("100100")); }); + it("should emit SwapPerpsForUSD", async function () { + const { billBroker } = await loadFixture(setupContracts); + await updateFees(billBroker, { + perpToUSDSwapFeePercs: { + lower: percentageFP("0.1"), + upper: percentageFP("0.5"), + }, + }); + const r = await billBroker.reserveState.staticCall(); + await expect(billBroker.swapPerpsForUSD(perpFP("100"), usdFP("103"))) + .to.emit(billBroker, "SwapPerpsForUSD") + .withArgs(perpFP("100"), r); + }); }); describe("stable swap with protocol fees", function () { @@ -998,6 +1066,20 @@ describe("BillBroker", function () { expect(r.usdBalance).to.eq(usdFP("114895.35")); expect(r.perpBalance).to.eq(perpFP("100100")); }); + it("should emit SwapPerpsForUSD", async function () { + const { billBroker } = await loadFixture(setupContracts); + await updateFees(billBroker, { + perpToUSDSwapFeePercs: { + lower: percentageFP("0.1"), + upper: percentageFP("0.5"), + }, + protocolSwapSharePerc: percentageFP("0.1"), + }); + const r = await billBroker.reserveState.staticCall(); + await expect(billBroker.swapPerpsForUSD(perpFP("100"), usdFP("103"))) + .to.emit(billBroker, "SwapPerpsForUSD") + .withArgs(perpFP("100"), r); + }); }); describe("when swap pushes system outside soft bound", function () { @@ -1069,6 +1151,20 @@ describe("BillBroker", function () { billBroker.swapPerpsForUSD(perpFP("5000"), usdFP("4000")), ).to.be.revertedWithCustomError(billBroker, "UnacceptableSwap"); }); + it("should emit SwapPerpsForUSD", async function () { + const { billBroker } = await loadFixture(setupContracts); + await updateFees(billBroker, { + perpToUSDSwapFeePercs: { + lower: percentageFP("0.1"), + upper: percentageFP("0.5"), + }, + protocolSwapSharePerc: percentageFP("0.1"), + }); + const r = await billBroker.reserveState.staticCall(); + await expect(billBroker.swapPerpsForUSD(perpFP("5000"), usdFP("4000"))) + .to.emit(billBroker, "SwapPerpsForUSD") + .withArgs(perpFP("5000"), r); + }); }); describe("when the pool has only usd", function () { diff --git a/yarn.lock b/yarn.lock index e5863ebf..50ffa547 100644 --- a/yarn.lock +++ b/yarn.lock @@ -157,7 +157,7 @@ __metadata: ethers: ^6.6.0 ethers-v5: "npm:ethers@^5.7.0" ganache-cli: latest - hardhat: ^2.22.6 + hardhat: ^2.22.8 hardhat-gas-reporter: latest lodash: ^4.17.21 prettier: ^2.7.1 @@ -1341,10 +1341,10 @@ __metadata: languageName: node linkType: hard -"@nomicfoundation/edr-darwin-arm64@npm:0.4.2": - version: 0.4.2 - resolution: "@nomicfoundation/edr-darwin-arm64@npm:0.4.2" - checksum: 7835e998c2ef83924efac0694bb4392f6abf770dc7f935dd28abc1a291f830cade14750d83a46a3205338e4ddff943dda60a9849317cf42edd38d7a2ce843588 +"@nomicfoundation/edr-darwin-arm64@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-darwin-arm64@npm:0.5.2" + checksum: f6ab386603c6e080fe7f611b739eb6d1d6a370220318b725cb582702563577150b3be14b6d0be71cb72bdb854e6992c587ecfc6833216f750eae8e7cd96de46f languageName: node linkType: hard @@ -1355,10 +1355,10 @@ __metadata: languageName: node linkType: hard -"@nomicfoundation/edr-darwin-x64@npm:0.4.2": - version: 0.4.2 - resolution: "@nomicfoundation/edr-darwin-x64@npm:0.4.2" - checksum: 94daa26610621e85cb025feb37bb93e9b89c59f908bf3eae70720d2b86632dbb1236420ae3ae6f685d563ba52519d5f860e68ccd898fa1fced831961dea2c08a +"@nomicfoundation/edr-darwin-x64@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-darwin-x64@npm:0.5.2" + checksum: 6f91f6d0294c0450e0501983f1de34a48582fe93f48428bc4b798ac93bb5483a96d626c2b4c62ac91102f00c826a3f9bfa16d748301440ebe1bbb2920ba3ba6d languageName: node linkType: hard @@ -1369,10 +1369,10 @@ __metadata: languageName: node linkType: hard -"@nomicfoundation/edr-linux-arm64-gnu@npm:0.4.2": - version: 0.4.2 - resolution: "@nomicfoundation/edr-linux-arm64-gnu@npm:0.4.2" - checksum: a7181e237f6ece8bd97e0f75972044dbf584c506bbac5bef586d9f7d627a2c07a279a2d892837bbedc80ea3dfb39fa66becc297238b5d715a942eed2a50745cd +"@nomicfoundation/edr-linux-arm64-gnu@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-linux-arm64-gnu@npm:0.5.2" + checksum: bd84cc2741bb2be3c3a60bae9dbb8ca7794a68b8675684c97f2c6e7310e5cba7efd1cf18d392d42481cda83fb478f83c0bd605104c5cf08bab44ec07669c3010 languageName: node linkType: hard @@ -1383,10 +1383,10 @@ __metadata: languageName: node linkType: hard -"@nomicfoundation/edr-linux-arm64-musl@npm:0.4.2": - version: 0.4.2 - resolution: "@nomicfoundation/edr-linux-arm64-musl@npm:0.4.2" - checksum: 5a849484b7a104a7e1497774c4117afc58f64d57d30889d4f6f676dddb5c695192c0789b8be0b71171a2af770167a28aa301ae3ece7a2a156d82d94388639b66 +"@nomicfoundation/edr-linux-arm64-musl@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-linux-arm64-musl@npm:0.5.2" + checksum: e7f7d82f16be1b26805bd90964c456aecb4a6a1397e87d507810d37bd383064271fa63932564e725fdb30868925334338ec459fe32f84fc11206644b7b37825c languageName: node linkType: hard @@ -1397,10 +1397,10 @@ __metadata: languageName: node linkType: hard -"@nomicfoundation/edr-linux-x64-gnu@npm:0.4.2": - version: 0.4.2 - resolution: "@nomicfoundation/edr-linux-x64-gnu@npm:0.4.2" - checksum: 0520dd9a583976fd0f49dfe6c23227f03cd811a395dc5eed1a2922b4358d7c71fdcfea8f389d4a0e23b4ec53e1435959a544380f94e48122a75f94a42b177ac7 +"@nomicfoundation/edr-linux-x64-gnu@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-linux-x64-gnu@npm:0.5.2" + checksum: ec025bf75227638b6b2cd01b7ba01b3ddaddf54c4d18d25e9d0364ac621981be2aaf124f4e60a88da5c9e267adb41a660a42668e2d6c9a6a57e55e8671fc76f1 languageName: node linkType: hard @@ -1411,10 +1411,10 @@ __metadata: languageName: node linkType: hard -"@nomicfoundation/edr-linux-x64-musl@npm:0.4.2": - version: 0.4.2 - resolution: "@nomicfoundation/edr-linux-x64-musl@npm:0.4.2" - checksum: 80c3b4346d8c27539bc005b09db233dedd8930310d1a049827661e69a8e03be9cbac27eb620a6ae9bfd46a2fbe22f83cee5af8d9e63178925d74d9c656246708 +"@nomicfoundation/edr-linux-x64-musl@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-linux-x64-musl@npm:0.5.2" + checksum: c9ff47f72645492383b2a598675878abc029b86325e2c457db1b2c4281916e11e4ef6336c355d40ae3c1736595bc43da51cfcf1e923464011f526f4db64c245b languageName: node linkType: hard @@ -1425,10 +1425,10 @@ __metadata: languageName: node linkType: hard -"@nomicfoundation/edr-win32-x64-msvc@npm:0.4.2": - version: 0.4.2 - resolution: "@nomicfoundation/edr-win32-x64-msvc@npm:0.4.2" - checksum: 736fb866fd5c2708560cbd5ae72815b5fc96e650cd74bc8bab0a1cb0e8baede4f595fdceb445c159814a6a7e8e691de227a5db49f61b3cd0ddfafd5715b397ab +"@nomicfoundation/edr-win32-x64-msvc@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-win32-x64-msvc@npm:0.5.2" + checksum: 56da7a1283470dede413cda5f2fef96e10250ec7a25562254ca0cd8045a653212c91e40fbcf37330e7af4e036d3c3aed83ea617831f9c7a5424389c73c53ed4e languageName: node linkType: hard @@ -1447,18 +1447,18 @@ __metadata: languageName: node linkType: hard -"@nomicfoundation/edr@npm:^0.4.1": - version: 0.4.2 - resolution: "@nomicfoundation/edr@npm:0.4.2" +"@nomicfoundation/edr@npm:^0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr@npm:0.5.2" dependencies: - "@nomicfoundation/edr-darwin-arm64": 0.4.2 - "@nomicfoundation/edr-darwin-x64": 0.4.2 - "@nomicfoundation/edr-linux-arm64-gnu": 0.4.2 - "@nomicfoundation/edr-linux-arm64-musl": 0.4.2 - "@nomicfoundation/edr-linux-x64-gnu": 0.4.2 - "@nomicfoundation/edr-linux-x64-musl": 0.4.2 - "@nomicfoundation/edr-win32-x64-msvc": 0.4.2 - checksum: 8c8457257b59ed9a29d88b7492e98e974d24e8318903e876a14dc0f6d5dc77948cd9053937d9730f54f920ba82ce3d244cab518d068359bcc20df88623f171ef + "@nomicfoundation/edr-darwin-arm64": 0.5.2 + "@nomicfoundation/edr-darwin-x64": 0.5.2 + "@nomicfoundation/edr-linux-arm64-gnu": 0.5.2 + "@nomicfoundation/edr-linux-arm64-musl": 0.5.2 + "@nomicfoundation/edr-linux-x64-gnu": 0.5.2 + "@nomicfoundation/edr-linux-x64-musl": 0.5.2 + "@nomicfoundation/edr-win32-x64-msvc": 0.5.2 + checksum: 336b1c7cad96fa78410f0c9cc9524abe9fb1e56384fe990b98bfd17f15f25b4665ad8f0525ccd9511f7c19173841fe712d50db993078629e2fc4047fda4665dc languageName: node linkType: hard @@ -9321,13 +9321,13 @@ __metadata: languageName: node linkType: hard -"hardhat@npm:^2.22.6": - version: 2.22.6 - resolution: "hardhat@npm:2.22.6" +"hardhat@npm:^2.22.8": + version: 2.22.8 + resolution: "hardhat@npm:2.22.8" dependencies: "@ethersproject/abi": ^5.1.2 "@metamask/eth-sig-util": ^4.0.0 - "@nomicfoundation/edr": ^0.4.1 + "@nomicfoundation/edr": ^0.5.2 "@nomicfoundation/ethereumjs-common": 4.0.4 "@nomicfoundation/ethereumjs-tx": 5.0.4 "@nomicfoundation/ethereumjs-util": 9.0.4 @@ -9378,7 +9378,7 @@ __metadata: optional: true bin: hardhat: internal/cli/bootstrap.js - checksum: 5aec1824db3575d63754de18c2629bcd820bc836d836f8a6346bcd9aa2ae4c397e090c43ea482ee765b704e018001015b5c84c5ded301a6a1144129c1a4c509b + checksum: 3731b510540f800b3b931e3ad7db4510dbd35eff18f34382875778db43de2a5ce1c52596c392aa694324f66392e844fede85954ab3cdc08df3da10f2a810135f languageName: node linkType: hard