Skip to content
This repository has been archived by the owner on Oct 26, 2022. It is now read-only.

Commit

Permalink
Merge branch 'master' into v0.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
boringcrypto committed Feb 1, 2021
2 parents 38bab8d + a8ae213 commit ff0d10a
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 81 deletions.
49 changes: 23 additions & 26 deletions contracts/BentoBoxPlus.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ contract BentoBoxPlus is MasterContractManager, BoringBatchable, IERC3156BatchFl
wethToken = wethToken_;
}

function toShare(IERC20 token, uint256 amount) external view returns(uint256 share) {
share = totals[token].toBase(amount);
function toShare(IERC20 token, uint256 amount, bool roundUp) external view returns(uint256 share) {
share = totals[token].toBase(amount, roundUp);
}

function toAmount(IERC20 token, uint256 share) external view returns(uint256 amount) {
amount = totals[token].toElastic(share);
function toAmount(IERC20 token, uint256 share, bool roundUp) external view returns(uint256 amount) {
amount = totals[token].toElastic(share, roundUp);
}

// M1 - M5: OK
Expand Down Expand Up @@ -98,33 +98,28 @@ contract BentoBoxPlus is MasterContractManager, BoringBatchable, IERC3156BatchFl
IERC20 token_, address from, address to, uint256 amount, uint256 share
) public payable allowed(from) returns (uint256 amountOut, uint256 shareOut) {
// Checks
require(to != address(0) || from == address(this), "BentoBox: to not set"); // To avoid a bad UI from burning funds
require(to != address(0), "BentoBox: to not set"); // To avoid a bad UI from burning funds

// Effects
IERC20 token = token_ == IERC20(0) ? wethToken : token_;
Rebase memory total = totals[token];

// Skim
if (from == address(this)) {
// S1 - S4: OK
// REENT: token.balanceOf(this) + strategy[token].balance <= total.amount
amount = token_ == IERC20(0) ? address(this).balance : _tokenBalanceOf(token).sub(total.elastic);
share = 0;
}

// S1 - S4: OK
// If a new token gets added, the tokenSupply call checks that this is a deployed contract. Needed for security.
require(total.elastic != 0 || token.totalSupply() > 0, "BentoBox: No tokens");
if (share == 0) {
share = total.toBase(amount);
// value of the share may be lower than the amount due to rounding, that's ok
share = total.toBase(amount, false);
} else {
amount = total.toElastic(share);
// amount may be lower than the value of share due to rounding, in that case, add 1 to amount (Always round up)
amount = total.toElastic(share, true);
}

// If to is not address(0) add the share, otherwise skip this to take profit
if (to != address(0)) {
balanceOf[token][to] = balanceOf[token][to].add(share);
total.base = total.base.add(share.to128());
}
// In case of skimming, check that only the skimmable amount is taken. For ETH, the full balance is available, so no need to check.
require(from != address(this) || token_ == IERC20(0) || amount <= _tokenBalanceOf(token).sub(total.elastic), "BentoBox: Skim too much");

balanceOf[token][to] = balanceOf[token][to].add(share);
total.base = total.base.add(share.to128());
total.elastic = total.elastic.add(amount.to128());
totals[token] = total;

Expand Down Expand Up @@ -158,9 +153,11 @@ contract BentoBoxPlus is MasterContractManager, BoringBatchable, IERC3156BatchFl
IERC20 token = token_ == IERC20(0) ? wethToken : token_;
Rebase memory total = totals[token];
if (share == 0) {
share = total.toBase(amount);
// value of the share paid could be lower than the amount paid due to rounding, in that case, add a share (Always round up)
share = total.toBase(amount, true);
} else {
amount = total.toElastic(share);
// amount may be lower than the value of share due to rounding, that's ok
amount = total.toElastic(share, false);
}

balanceOf[token][from] = balanceOf[token][from].sub(share);
Expand Down Expand Up @@ -243,7 +240,7 @@ contract BentoBoxPlus is MasterContractManager, BoringBatchable, IERC3156BatchFl

borrower.onFlashLoan(msg.sender, token, amount, fee, data); // REENT: Exit

require(_tokenBalanceOf(token) == totals[token].addElastic(fee.to128()), "BentoBoxPlus: Wrong amount");
require(_tokenBalanceOf(token) >= totals[token].addElastic(fee.to128()), "BentoBoxPlus: Wrong amount");
emit LogFlashLoan(address(borrower), token, amount, fee, receiver);
}

Expand Down Expand Up @@ -274,7 +271,7 @@ contract BentoBoxPlus is MasterContractManager, BoringBatchable, IERC3156BatchFl
for (uint256 i = 0; i < len; i++) {
IERC20 token = tokens[i];
// REENT: token.balanceOf(this) + strategy[token].balance <= total.amount
require(_tokenBalanceOf(token) == totals[token].addElastic(fees[i].to128()), "BentoBoxPlus: Wrong amount");
require(_tokenBalanceOf(token) >= totals[token].addElastic(fees[i].to128()), "BentoBoxPlus: Wrong amount");
emit LogFlashLoan(address(borrower), token, amounts[i], fees[i], receivers[i]);
}
}
Expand Down Expand Up @@ -310,11 +307,11 @@ contract BentoBoxPlus is MasterContractManager, BoringBatchable, IERC3156BatchFl
// Effects
if (amount > 0) {
uint256 add = uint256(amount);
totals[token].elastic = totals[token].elastic.add(add.to128());
totals[token].addElastic(add);
emit LogDeposit(token, address(from), address(this), add, 0);
} else if (amount < 0) {
uint256 sub = uint256(-amount);
totals[token].elastic = totals[token].elastic.sub(sub.to128());
totals[token].subElastic(sub);
emit LogWithdraw(token, address(this), address(from), sub, 0);
}
}
Expand Down
36 changes: 18 additions & 18 deletions contracts/LendingPair.sol
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ contract LendingPair is ERC20, BoringOwnable, IMasterContract {
accrueInfo = _accrueInfo; return;
}

uint256 totalAssetAmount = bentoBox.toAmount(asset, _totalAsset.elastic);
uint256 totalAssetAmount = bentoBox.toAmount(asset, _totalAsset.elastic, false);
if (_totalBorrow.elastic > 0) {
// Accrue interest
extraAmount = uint256(_totalBorrow.elastic).mul(_accrueInfo.interestPerBlock).mul(blocks) / 1e18;
Expand Down Expand Up @@ -236,7 +236,8 @@ contract LendingPair is ERC20, BoringOwnable, IMasterContract {
open
? OPEN_COLLATERIZATION_RATE
: CLOSED_COLLATERIZATION_RATE
)
),
false
) >=
userBorrowPart[user]
.mul(_totalBorrow.elastic)
Expand Down Expand Up @@ -295,12 +296,10 @@ contract LendingPair is ERC20, BoringOwnable, IMasterContract {
function _addAsset(address to, bool skim, uint256 share) internal returns (uint256 fraction) {
Rebase memory _totalAsset = totalAsset;
uint256 totalAssetShare = _totalAsset.elastic;
uint256 allShare = _totalAsset.elastic + bentoBox.toShare(asset, totalBorrow.elastic);
uint256 allShare = _totalAsset.elastic + bentoBox.toShare(asset, totalBorrow.elastic, true);
fraction = allShare == 0 ? share : share.mul(_totalAsset.base) / allShare;
_totalAsset.elastic = _totalAsset.elastic.add(share.to128());
_totalAsset.base = _totalAsset.base.add(fraction.to128());
totalAsset = _totalAsset.add(share, fraction);
balanceOf[to] = balanceOf[to].add(fraction);
totalAsset = _totalAsset;
_addTokens(asset, share, totalAssetShare, skim);
emit LogAddAsset(skim ? address(bentoBox) : msg.sender, to, share, fraction);
}
Expand All @@ -312,7 +311,7 @@ contract LendingPair is ERC20, BoringOwnable, IMasterContract {

function _removeAsset(address to, uint256 fraction) internal returns (uint256 share) {
Rebase memory _totalAsset = totalAsset;
uint256 allShare = _totalAsset.elastic + bentoBox.toShare(asset, totalBorrow.elastic);
uint256 allShare = _totalAsset.elastic + bentoBox.toShare(asset, totalBorrow.elastic, true);
share = fraction.mul(allShare) / _totalAsset.base;
balanceOf[msg.sender] = balanceOf[msg.sender].sub(fraction);
_totalAsset.elastic = _totalAsset.elastic.sub(share.to128());
Expand All @@ -330,11 +329,11 @@ contract LendingPair is ERC20, BoringOwnable, IMasterContract {
function _borrow(address to, uint256 amount) internal returns (uint256 part, uint256 share) {
uint256 feeAmount = amount.mul(BORROW_OPENING_FEE) / BORROW_OPENING_FEE_PRECISION; // A flat % fee is charged for any borrow

(totalBorrow, part) = totalBorrow.add(amount.add(feeAmount));
(totalBorrow, part) = totalBorrow.add(amount.add(feeAmount), true);
userBorrowPart[to] = userBorrowPart[to].add(part);
emit LogBorrow(msg.sender, to, amount.add(feeAmount), part);

share = bentoBox.toShare(asset, amount);
share = bentoBox.toShare(asset, amount, false);
totalAsset.elastic = totalAsset.elastic.sub(share.to128());
bentoBox.transfer(asset, address(this), to, share);
}
Expand All @@ -345,10 +344,10 @@ contract LendingPair is ERC20, BoringOwnable, IMasterContract {
}

function _repay(address to, bool skim, uint256 part) internal returns (uint256 amount) {
(totalBorrow, amount) = totalBorrow.sub(part);
(totalBorrow, amount) = totalBorrow.sub(part, true);
userBorrowPart[to] = userBorrowPart[to].sub(part);

uint256 share = bentoBox.toShare(asset, amount);
uint256 share = bentoBox.toShare(asset, amount, true);
uint128 totalShare = totalAsset.elastic;
_addTokens(asset, share, uint256(totalShare), skim);
totalAsset.elastic = totalShare.add(share.to128());
Expand All @@ -372,7 +371,7 @@ contract LendingPair is ERC20, BoringOwnable, IMasterContract {
uint8 constant internal ACTION_BENTO_TRANSFER = 22;
uint8 constant internal ACTION_BENTO_TRANSFER_MULTIPLE = 23;
uint8 constant internal ACTION_BENTO_SETAPPROVAL = 24;
uint8 constant internal ACTION_GET_REPAY_SHARE = 40;
uint8 constant internal ACTION_GET_REPAY_AMOUNT = 40;
uint8 constant internal ACTION_GET_REPAY_PART = 41;

int256 constant internal USE_VALUE1 = -1;
Expand Down Expand Up @@ -490,13 +489,13 @@ contract LendingPair is ERC20, BoringOwnable, IMasterContract {
if (returnValues == 1) { (value1) = abi.decode(returnData, (uint256)); }
else if (returnValues == 2) { (value1, value2) = abi.decode(returnData, (uint256, uint256)); }

} else if (action == ACTION_GET_REPAY_SHARE) {
} else if (action == ACTION_GET_REPAY_AMOUNT) {
(int256 part) = abi.decode(datas[i], (int256));
value1 = totalBorrow.toElastic(_num(part, value1, value2));
value1 = totalBorrow.toElastic(_num(part, value1, value2), true);

} else if (action == ACTION_GET_REPAY_PART) {
(int256 share) = abi.decode(datas[i], (int256));
value1 = totalBorrow.toBase(_num(share, value1, value2));
(int256 amount) = abi.decode(datas[i], (int256));
value1 = totalBorrow.toBase(_num(amount, value1, value2), false);
}
}

Expand All @@ -518,9 +517,10 @@ contract LendingPair is ERC20, BoringOwnable, IMasterContract {
address user = users[i];
if (!isSolvent(user, open)) {
uint256 borrowPart = borrowParts[i];
uint256 borrowAmount = _totalBorrow.toElastic(borrowPart);
uint256 borrowAmount = _totalBorrow.toElastic(borrowPart, false);
uint256 collateralShare = bentoBox.toShare(collateral, borrowAmount
.mul(LIQUIDATION_MULTIPLIER).mul(exchangeRate) / (LIQUIDATION_MULTIPLIER_PRECISION * EXCHANGE_RATE_PRECISION));
.mul(LIQUIDATION_MULTIPLIER).mul(exchangeRate) / (LIQUIDATION_MULTIPLIER_PRECISION * EXCHANGE_RATE_PRECISION),
false);

userCollateralShare[user] = userCollateralShare[user].sub(collateralShare);
userBorrowPart[user] = userBorrowPart[user].sub(borrowPart);
Expand Down
2 changes: 1 addition & 1 deletion contracts/swappers/SushiSwapSwapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ contract SushiSwapSwapper is ISwapper {
IUniswapV2Pair pair = IUniswapV2Pair(factory.getPair(address(fromToken), address(toToken)));
(uint256 reserve0, uint256 reserve1,) = pair.getReserves();

uint256 amountToExact = bentoBox.toAmount(toToken, shareToExact);
uint256 amountToExact = bentoBox.toAmount(toToken, shareToExact, true);

uint256 amountFrom;
if (pair.token0() == address(fromToken)) {
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"name": "@sushiswap/bentobox",
"version": "0.3.0",
"version": "1.0.0",
"private": true,
"description": "BentoBox",
"main": "index.js",
"files": [
"contracts",
"deployments"
],
],
"directories": {
"test": "test"
},
Expand Down Expand Up @@ -80,6 +81,6 @@
"solidity-coverage": "^0.7.12"
},
"dependencies": {
"@boringcrypto/boring-solidity": "boringcrypto/BoringSolidity#e06e943"
"@boringcrypto/boring-solidity": "boringcrypto/BoringSolidity#011d109"
}
}
}
30 changes: 10 additions & 20 deletions test/BentoBoxPlus.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ describe("BentoBoxPlus", function () {

describe("Conversion", function () {
it("Should convert Shares to Amounts", async function () {
expect(await this.bentoBox.toShare(this.a.address, 1)).to.be.equal(1)
expect(await this.bentoBox.toShare(this.a.address, 1, false)).to.be.equal(1)
})
it("Should convert amount to shares", async function () {
expect(await this.bentoBox.toAmount(this.a.address, 1)).to.be.equal(1)
expect(await this.bentoBox.toAmount(this.a.address, 1, false)).to.be.equal(1)
})
})
describe("Deposit", function () {
Expand Down Expand Up @@ -330,16 +330,6 @@ describe("BentoBoxPlus", function () {
expect(await this.bentoBox.balanceOf(this.a.address, this.bob.address), "bob should have tokens").to.be.equal(1)
})

it("Be benevolent", async function () {
await this.a.transfer(this.bentoBox.address, 1)

expect(await this.bentoBox.balanceOf(this.a.address, this.bob.address), "bob should have no tokens").to.be.equal(0)

await this.bentoBox.connect(this.bob).deposit(this.a.address, this.bentoBox.address, ADDRESS_ZERO, 1, 0)

expect((await this.bentoBox.totals(this.a.address)).elastic, "total amount should increase").to.be.equal(1)
})

it("Emits LogDeposit event with expected arguments", async function () {
await this.a.transfer(this.bentoBox.address, 1)

Expand Down Expand Up @@ -447,7 +437,7 @@ describe("BentoBoxPlus", function () {
})
describe("FlashLoan", function () {
it("should revert on batch flashloan if not enough funds are available", async function () {
const param = this.bentoBox.interface.encodeFunctionData("toShare", [this.a.address, 1])
const param = this.bentoBox.interface.encodeFunctionData("toShare", [this.a.address, 1, false])
await expect(
this.bentoBox.batchFlashLoan(this.flashLoaner.address, [this.flashLoaner.address], [this.a.address], [getBigNumber(1)], param)
).to.be.revertedWith("BoringERC20: Transfer failed")
Expand All @@ -457,7 +447,7 @@ describe("BentoBoxPlus", function () {
await this.a.transfer(this.bentoBox.address, getBigNumber(2))
await this.a.approve(this.bentoBox.address, getBigNumber(2))
await this.bentoBox.deposit(this.a.address, this.alice.address, this.alice.address, getBigNumber(1), 0)
const param = this.bentoBox.interface.encodeFunctionData("toShare", [this.a.address, 1])
const param = this.bentoBox.interface.encodeFunctionData("toShare", [this.a.address, 1, false])
await expect(
this.bentoBox.batchFlashLoan(this.flashLoaner.address, [this.flashLoaner.address], [this.a.address], [getBigNumber(1)], param)
).to.be.revertedWith("BoringERC20: Transfer")
Expand All @@ -466,7 +456,7 @@ describe("BentoBoxPlus", function () {
it("should revert on flashloan if amount is not paid back", async function () {
await this.a.approve(this.bentoBox.address, getBigNumber(2))
await this.bentoBox.deposit(this.a.address, this.alice.address, this.alice.address, getBigNumber(1), 0)
const param = this.bentoBox.interface.encodeFunctionData("toShare", [this.a.address, 1])
const param = this.bentoBox.interface.encodeFunctionData("toShare", [this.a.address, 1, false])
await expect(
this.bentoBox.flashLoan(this.sneakyFlashLoaner.address, this.sneakyFlashLoaner.address, this.a.address, getBigNumber(1), param)
).to.be.revertedWith("BentoBoxPlus: Wrong amount")
Expand All @@ -475,7 +465,7 @@ describe("BentoBoxPlus", function () {
it("should revert on batch flashloan if amount is not paid back", async function () {
await this.a.approve(this.bentoBox.address, getBigNumber(2))
await this.bentoBox.deposit(this.a.address, this.alice.address, this.alice.address, getBigNumber(1), 0)
const param = this.bentoBox.interface.encodeFunctionData("toShare", [this.a.address, 1])
const param = this.bentoBox.interface.encodeFunctionData("toShare", [this.a.address, 1, false])
await expect(
this.bentoBox.batchFlashLoan(
this.sneakyFlashLoaner.address,
Expand Down Expand Up @@ -504,20 +494,20 @@ describe("BentoBoxPlus", function () {
await this.a.approve(this.bentoBox.address, getBigNumber(2))
await this.bentoBox.deposit(this.a.address, this.alice.address, this.alice.address, 0, getBigNumber(1))

const param = this.bentoBox.interface.encodeFunctionData("toShare", [this.a.address, 1])
const param = this.bentoBox.interface.encodeFunctionData("toShare", [this.a.address, 1, false])
await this.a.transfer(this.flashLoaner.address, getBigNumber(2))
await this.bentoBox.flashLoan(this.flashLoaner.address, this.flashLoaner.address, this.a.address, getBigNumber(1), param)
expect(await this.bentoBox.toAmount(this.a.address, getBigNumber(1))).to.be.equal(getBigNumber(1).mul(10005).div(10000))
expect(await this.bentoBox.toAmount(this.a.address, getBigNumber(1), false)).to.be.equal(getBigNumber(1).mul(10005).div(10000))
})

it("should allow batch flashloan", async function () {
await this.a.approve(this.bentoBox.address, getBigNumber(2))
await this.bentoBox.deposit(this.a.address, this.alice.address, this.alice.address, 0, getBigNumber(1))

const param = this.bentoBox.interface.encodeFunctionData("toShare", [this.a.address, 1])
const param = this.bentoBox.interface.encodeFunctionData("toShare", [this.a.address, 1, false])
await this.a.transfer(this.flashLoaner.address, getBigNumber(2))
await this.bentoBox.batchFlashLoan(this.flashLoaner.address, [this.flashLoaner.address], [this.a.address], [getBigNumber(1)], param)
expect(await this.bentoBox.toAmount(this.a.address, getBigNumber(1))).to.be.equal(getBigNumber(1).mul(10005).div(10000))
expect(await this.bentoBox.toAmount(this.a.address, getBigNumber(1), false)).to.be.equal(getBigNumber(1).mul(10005).div(10000))
})
})

Expand Down
3 changes: 1 addition & 2 deletions test/ERC20.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,7 @@ describe("ERC20", function () {
})

const allowance01 = await this.token.allowance(this.alice.address, this.bob.address)
const maxMinus20 = "115792089237316195423570985008687907853269984665640564039457584007913129639915"
expect(allowance01).to.equal(maxMinus20)
expect(allowance01).to.equal(max)

const balance22 = await this.token.balanceOf(this.carol.address)
expect(balance22).to.equal(20)
Expand Down
Loading

0 comments on commit ff0d10a

Please sign in to comment.