Skip to content

Commit

Permalink
Post price fix and add cap balance check (#102)
Browse files Browse the repository at this point in the history
* fix: Make local price adjust in same ratio with swap slippage

* feat: add cap balance check

* chore: run tests ok

* chore: add capBal revert tests

* chore: update revert msgs, add test case

---------

Co-authored-by: cphilo <yacphilo@gmail.com>
  • Loading branch information
fb-alexcq and Cphilo committed Mar 7, 2024
1 parent fe978e6 commit f5fe28a
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 16 deletions.
36 changes: 23 additions & 13 deletions contracts/WooPPV2.sol
Expand Up @@ -64,6 +64,7 @@ contract WooPPV2 is Ownable, ReentrancyGuard, Pausable, IWooPPV2 {
struct TokenInfo {
uint192 reserve; // balance reserve
uint16 feeRate; // 1 in 100000; 10 = 1bp = 0.01%; max = 65535
uint192 capBal; // balance cap
}

/* ----- State variables ----- */
Expand Down Expand Up @@ -204,6 +205,19 @@ contract WooPPV2 is Ownable, ReentrancyGuard, Pausable, IWooPPV2 {
tokenInfos[token].feeRate = rate;
}

function setCapBal(address token, uint192 capBal) external onlyAdmin {
tokenInfos[token].capBal = capBal;
}

function setTokenInfo(
address token,
uint16 _feeRate,
uint192 _capBal
) external onlyAdmin {
tokenInfos[token].feeRate = _feeRate;
tokenInfos[token].capBal = _capBal;
}

function pause() external onlyAdmin {
super._pause();
}
Expand Down Expand Up @@ -369,7 +383,8 @@ contract WooPPV2 is Ownable, ReentrancyGuard, Pausable, IWooPPV2 {
require(to != address(0), "WooPPV2: !to");

This comment has been minimized.

Copy link
@joaolbarba88

joaolbarba88 Mar 17, 2024

Uber desbloquear algoritmo sistema chamadas e valores

require(baseToken != quoteToken, "WooPPV2: baseToken==quoteToken");

require(balance(baseToken) - tokenInfos[baseToken].reserve >= baseAmount, "WooPPV2: BASE_BALANCE_NOT_ENOUGH");
require(balance(baseToken) <= tokenInfos[baseToken].capBal, "WooPPV2: !CAP");
require(balance(baseToken) - tokenInfos[baseToken].reserve >= baseAmount, "WooPPV2: !BASE");

{
uint256 newPrice;
Expand Down Expand Up @@ -416,10 +431,8 @@ contract WooPPV2 is Ownable, ReentrancyGuard, Pausable, IWooPPV2 {
require(to != address(0), "WooPPV2: !to");
require(baseToken != quoteToken, "WooPPV2: baseToken==quoteToken");

require(
balance(quoteToken) - tokenInfos[quoteToken].reserve >= quoteAmount,
"WooPPV2: QUOTE_BALANCE_NOT_ENOUGH"
);
require(balance(quoteToken) <= tokenInfos[quoteToken].capBal, "WooPPV2: !CAP");
require(balance(quoteToken) - tokenInfos[quoteToken].reserve >= quoteAmount, "WooPPV2: !QUOTE");

uint256 swapFee = (quoteAmount * tokenInfos[baseToken].feeRate) / 1e5;
quoteAmount = quoteAmount - swapFee;
Expand Down Expand Up @@ -466,6 +479,7 @@ contract WooPPV2 is Ownable, ReentrancyGuard, Pausable, IWooPPV2 {
require(baseToken2 != address(0) && baseToken2 != quoteToken, "WooPPV2: !baseToken2");
require(to != address(0), "WooPPV2: !to");

require(balance(baseToken1) <= tokenInfos[baseToken1].capBal, "WooPPV2: !CAP");
require(balance(baseToken1) - tokenInfos[baseToken1].reserve >= base1Amount, "WooPPV2: !BASE1_BALANCE");

IWooracleV2.State memory state1 = IWooracleV2(wooracle).state(baseToken1);
Expand Down Expand Up @@ -549,10 +563,9 @@ contract WooPPV2 is Ownable, ReentrancyGuard, Pausable, IWooPPV2 {
quoteAmount = (((baseAmount * decs.quoteDec * state.price) / decs.priceDec) * coef) / 1e18 / decs.baseDec;
}

// newPrice = oracle.price * (1 - 2 * k * oracle.price * baseAmount)
// newPrice = oracle.price * (1 - k * oracle.price * baseAmount)
newPrice =
((uint256(1e18) - (uint256(2) * state.coeff * state.price * baseAmount) / decs.priceDec / decs.baseDec) *
state.price) /
((uint256(1e18) - (state.coeff * state.price * baseAmount) / decs.priceDec / decs.baseDec) * state.price) /
1e18;
}

Expand All @@ -571,11 +584,8 @@ contract WooPPV2 is Ownable, ReentrancyGuard, Pausable, IWooPPV2 {
baseAmount = (((quoteAmount * decs.baseDec * decs.priceDec) / state.price) * coef) / 1e18 / decs.quoteDec;
}

// new_price = oracle.price * (1 + 2 * k * quoteAmount)
newPrice =
((uint256(1e18) * decs.quoteDec + uint256(2) * state.coeff * quoteAmount) * state.price) /
decs.quoteDec /
1e18;
// new_price = oracle.price * (1 + k * quoteAmount)
newPrice = ((uint256(1e18) * decs.quoteDec + state.coeff * quoteAmount) * state.price) / decs.quoteDec / 1e18;
}

function _maxUInt16(uint16 a, uint16 b) private pure returns (uint16) {
Expand Down
4 changes: 4 additions & 0 deletions test/typescript/Integration_WooPP_Fee_Rebate_Vault.test.ts
Expand Up @@ -160,6 +160,10 @@ describe("Rebate Fee Vault Integration Test", () => {
await wooPP.setFeeRate(btcToken.address, BASE_FEE_RATE);
await wooPP.setFeeRate(wooToken.address, BASE_FEE_RATE);

await wooPP.setCapBal(btcToken.address, ONE.mul(200));
await wooPP.setCapBal(usdtToken.address, ONE.mul(20000000));
await wooPP.setCapBal(wooToken.address, ONE.mul(20000000));

wooRouter = (await deployContract(owner, WooRouterV2Artifact, [wethToken.address, wooPP.address])) as WooRouterV2;

await rebateManager.setWooRouter(wooRouter.address);
Expand Down
96 changes: 93 additions & 3 deletions test/typescript/WooPPv2.test.ts
Expand Up @@ -32,7 +32,7 @@
*/

import { expect, use } from "chai";
import { Contract, utils } from "ethers";
import { BigNumber, Contract, utils } from "ethers";
import { ethers } from "hardhat";
import { deployContract, solidity } from "ethereum-waffle";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
Expand Down Expand Up @@ -178,12 +178,19 @@ describe("WooPPV2 Integration tests", () => {

describe("wooPP swap", () => {
let wooPP: WooPPV2;
let btcCapBal: BigNumber;
let usdtCapBal: BigNumber;

beforeEach("Deploy WooPPV2", async () => {
wooPP = (await deployContract(owner, WooPPV2Artifact, [usdtToken.address])) as WooPPV2;

await wooPP.init(wooracle.address, feeAddr.address);
await wooPP.setFeeRate(btcToken.address, 100);
btcCapBal = ONE.mul(100);
usdtCapBal = ONE.mul(1000000);

await wooPP.setCapBal(btcToken.address, btcCapBal);
await wooPP.setCapBal(usdtToken.address, usdtCapBal);

await btcToken.mint(owner.address, ONE.mul(10));
await usdtToken.mint(owner.address, ONE.mul(300000));
Expand All @@ -206,6 +213,7 @@ describe("WooPPV2 Integration tests", () => {
});

it("sellBase accuracy1", async () => {

await btcToken.mint(user1.address, ONE.mul(3));
const preUserUsdt = await usdtToken.balanceOf(user1.address);
const preUserBtc = await btcToken.balanceOf(user1.address);
Expand Down Expand Up @@ -303,7 +311,7 @@ describe("WooPPV2 Integration tests", () => {

it("sellBase fail1", async () => {
await expect(wooPP.swap(btcToken.address, quote.address, ONE, 0, user2.address, ZERO_ADDR)).to.be.revertedWith(
"WooPPV2: BASE_BALANCE_NOT_ENOUGH"
"WooPPV2: !BASE"
);

await expect(wooPP.swap(ZERO_ADDR, quote.address, ONE, 0, user2.address, ZERO_ADDR)).to.be.revertedWith(
Expand All @@ -327,6 +335,26 @@ describe("WooPPV2 Integration tests", () => {
).to.be.revertedWith("WooPPV2: quoteAmount_LT_minQuoteAmount");
});

it("sellBase cap fail", async() => {
const bal = await wooPP.balance(btcToken.address);
const addAmount = ONE.mul(2);
await btcToken.mint(user2.address, addAmount);

const baseAmount = ONE;
await wooPP.setCapBal(btcToken.address, bal.add(baseAmount));

await btcToken.connect(user2).approve(wooPP.address, baseAmount);
await btcToken.connect(user2).transfer(wooPP.address, baseAmount);
await wooPP.swap(btcToken.address, quote.address, baseAmount, 0, user2.address, ZERO_ADDR);

await btcToken.connect(user2).approve(wooPP.address, baseAmount);
await btcToken.connect(user2).transfer(wooPP.address, baseAmount);
await expect(wooPP.swap(btcToken.address, quote.address, baseAmount, 0, user2.address, ZERO_ADDR)).to.be.revertedWith(
"WooPPV2: !CAP"
);
await wooPP.setCapBal(btcToken.address, btcCapBal);
});

it("sellQuote accuracy1", async () => {
await btcToken.mint(user1.address, ONE.mul(3));
await usdtToken.mint(user1.address, ONE.mul(100000));
Expand Down Expand Up @@ -428,7 +456,7 @@ describe("WooPPV2 Integration tests", () => {
it("sellQuote fail1", async () => {
const quoteAmount = ONE.mul(20000);
await expect(wooPP.swap(quote.address, btcToken.address, quoteAmount, 0, user2.address, ZERO_ADDR)).to.be.revertedWith(
"WooPPV2: QUOTE_BALANCE_NOT_ENOUGH"
"WooPPV2: !QUOTE"
);

await expect(wooPP.swap(quote.address, ZERO_ADDR, quoteAmount, 0, user2.address, ZERO_ADDR)).to.be.revertedWith(
Expand All @@ -453,6 +481,25 @@ describe("WooPPV2 Integration tests", () => {
).to.be.revertedWith("WooPPV2: baseAmount_LT_minBaseAmount");
});

it("sellQuote cap fail", async() => {
const bal = await wooPP.balance(quote.address);
const quoteAmount = ONE.mul(100);
const addAmount = quoteAmount.mul(2);
await usdtToken.mint(user1.address, addAmount);

await wooPP.setCapBal(quote.address, bal.add(quoteAmount));
await usdtToken.connect(user1).approve(wooPP.address, quoteAmount);
await usdtToken.connect(user1).transfer(wooPP.address, quoteAmount);
await wooPP.swap(quote.address, btcToken.address, quoteAmount, 0, user1.address, ZERO_ADDR);

await usdtToken.connect(user1).approve(wooPP.address, quoteAmount);
await usdtToken.connect(user1).transfer(wooPP.address, quoteAmount);
await expect(wooPP.swap(quote.address, btcToken.address, quoteAmount, 0, user1.address, ZERO_ADDR)).to.be.revertedWith(
"WooPPV2: !CAP"
);
await wooPP.setCapBal(usdtToken.address, usdtCapBal);
});

it("balance accuracy", async () => {
const bal1 = await wooPP.balance(usdtToken.address);
const bal2 = await wooPP.balance(btcToken.address);
Expand Down Expand Up @@ -492,6 +539,8 @@ describe("WooPPV2 Integration tests", () => {

await wooPP.init(wooracle.address, feeAddr.address);
await wooPP.setFeeRate(btcToken.address, 100);
await wooPP.setCapBal(btcToken.address, ONE.mul(100));
await wooPP.setCapBal(usdtToken.address, ONE.mul(1000000));

await btcToken.mint(owner.address, ONE.mul(10));
await usdtToken.mint(owner.address, ONE.mul(300000));
Expand Down Expand Up @@ -619,13 +668,23 @@ describe("WooPPV2 Integration tests", () => {

describe("BaseToBase Functions", () => {
let wooPP: WooPPV2;
let btcCapBal: BigNumber;
let usdtCapBal: BigNumber;
let wooCapBal: BigNumber;

beforeEach("Deploy wooPPV2", async () => {
wooPP = (await deployContract(owner, WooPPV2Artifact, [usdtToken.address])) as WooPPV2;

await wooPP.init(wooracle.address, feeAddr.address);
await wooPP.setFeeRate(btcToken.address, 100);

btcCapBal = ONE.mul(100);
usdtCapBal = ONE.mul(1000000);
wooCapBal = ONE.mul(2000000);
await wooPP.setCapBal(btcToken.address, btcCapBal);
await wooPP.setCapBal(usdtToken.address, usdtCapBal);
await wooPP.setCapBal(wooToken.address, wooCapBal);

// await btcToken.approve(wooPP.address, ONE.mul(10))
// await wooPP.deposit(btcToken.address, ONE.mul(10))

Expand Down Expand Up @@ -890,6 +949,7 @@ describe("WooPPV2 Integration tests", () => {

await wooToken.connect(user1).approve(wooPP.address, base1Amount);
await wooToken.connect(user1).transfer(wooPP.address, base1Amount);

await expect(
wooPP
.connect(user1)
Expand Down Expand Up @@ -923,6 +983,36 @@ describe("WooPPV2 Integration tests", () => {
.swap(wooToken.address, btcToken.address, base1Amount, minBase2Amount, user1.address, ZERO_ADDR)
).to.be.reverted;
});
it("swapBaseToBase cap fail", async () => {
_clearUser1Balance();

await wooToken.approve(wooPP.address, ONE.mul(1000000));
await wooPP.deposit(wooToken.address, ONE.mul(1000000));

await quote.approve(wooPP.address, ONE.mul(10000));
await wooPP.deposit(quote.address, ONE.mul(10000));

const base1Amount = ONE;
const minBase2Amount = base1Amount.mul(BTC_PRICE).mul(100).div(15).mul(997).div(1000);

const addAmount = base1Amount.mul(2);
await btcToken.mint(user1.address, addAmount);
const btcBal = await wooPP.balance(btcToken.address);
await wooPP.setCapBal(btcToken.address, btcBal.add(base1Amount));

await btcToken.connect(user1).approve(wooPP.address, base1Amount);
await btcToken.connect(user1).transfer(wooPP.address, base1Amount);
await wooPP
.swap(btcToken.address, wooToken.address, base1Amount, minBase2Amount, user1.address, ZERO_ADDR);

await btcToken.connect(user1).approve(wooPP.address, base1Amount);
await btcToken.connect(user1).transfer(wooPP.address, base1Amount);
await expect(wooPP
.swap(btcToken.address, wooToken.address, base1Amount, minBase2Amount, user1.address, ZERO_ADDR)).to.be.revertedWith(
"WooPPV2: !CAP"
);
await wooPP.setCapBal(wooToken.address, wooCapBal);
});
});

async function _clearUser1Balance() {
Expand Down
3 changes: 3 additions & 0 deletions test/typescript/WooRouterV2.test.ts
Expand Up @@ -194,6 +194,9 @@ describe("WooRouterV2 Integration Tests", () => {
wooPP = (await deployContract(owner, WooPPV2Artifact, [usdtToken.address])) as WooPPV2;

await wooPP.init(wooracle.address, feeAddr.address);
await wooPP.setCapBal(btcToken.address, ONE.mul(100));
await wooPP.setCapBal(usdtToken.address, ONE.mul(10000000));
await wooPP.setCapBal(wooToken.address, ONE.mul(10000000));

wooRouter = (await deployContract(owner, WooRouterV2Artifact, [WBNB_ADDR, wooPP.address])) as WooRouterV2;

Expand Down

0 comments on commit f5fe28a

Please sign in to comment.