Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pay callback #104

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion contracts/TimeswapFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {TimeswapPair} from './TimeswapPair.sol';

/// @title Timeswap Factory
/// @author Timeswap Labs
/// @notice It is recommnded to use Timeswap Convenience to interact with this contract.
/// @notice It is recommended to use Timeswap Convenience to interact with this contract.
/// @notice All error messages are coded and can be found in the documentation.
contract TimeswapFactory is IFactory {
/* ===== MODEL ===== */
Expand Down Expand Up @@ -69,6 +69,7 @@ contract TimeswapFactory is IFactory {
function acceptOwner() external override {
require(msg.sender == pendingOwner, 'E102');
owner = msg.sender;
pendingOwner = address(0);

emit AcceptOwner(msg.sender);
}
Expand Down
140 changes: 78 additions & 62 deletions contracts/TimeswapPair.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,17 @@ import {BurnMath} from './libraries/BurnMath.sol';
import {LendMath} from './libraries/LendMath.sol';
import {WithdrawMath} from './libraries/WithdrawMath.sol';
import {BorrowMath} from './libraries/BorrowMath.sol';
import {PayMath} from './libraries/PayMath.sol';
import {SafeTransfer} from './libraries/SafeTransfer.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {Array} from './libraries/Array.sol';
import {Callback} from './libraries/Callback.sol';
import {BlockNumber} from './libraries/BlockNumber.sol';

/// @title Timeswap Pair
/// @author Timeswap Labs
/// @notice It is recommnded to use Timeswap Convenience to interact with this contract.
/// @notice It is recommended to use Timeswap Convenience to interact with this contract.
/// @notice All error messages are coded and can be found in the documentation.
contract TimeswapPair is IPair {
using SafeTransfer for IERC20;
using SafeERC20 for IERC20;
using Array for Due[];

/* ===== MODEL ===== */
Expand All @@ -40,7 +39,7 @@ contract TimeswapPair is IPair {
mapping(uint256 => Pool) private pools;

/// @dev Stores the access state for reentrancy guard.
uint256 private locked;
uint256 private locked = 1;

/* ===== VIEW =====*/

Expand All @@ -49,11 +48,7 @@ contract TimeswapPair is IPair {
external
view
override
returns (
uint112 x,
uint112 y,
uint112 z
)
returns (uint112, uint112, uint112)
{
State memory state = pools[maturity].state;
return (state.x, state.y, state.z);
Expand Down Expand Up @@ -119,10 +114,10 @@ contract TimeswapPair is IPair {

/// @dev The modifier for reentrancy guard.
modifier lock() {
require(locked == 0, 'E211');
locked = 1;
require(locked == 1, 'E211');
locked = 2;
_;
locked = 0;
locked = 1;
}

/* ===== UPDATE ===== */
Expand All @@ -147,35 +142,36 @@ contract TimeswapPair is IPair {
)
{
require(block.timestamp < maturity, 'E202');
require(maturity - block.timestamp < 0x100000000, 'E208');
require(liquidityTo != address(0) && dueTo != address(0), 'E201');
require(liquidityTo != address(this) && dueTo != address(this), 'E204');
require(xIncrease > 0 && yIncrease > 0 && zIncrease > 0, 'E205');
unchecked { require(maturity - block.timestamp < 0x100000000, 'E208'); }
require(liquidityTo != address(0), 'E201');
require(dueTo != address(0), 'E201');
require(liquidityTo != address(this), 'E204');
require(dueTo != address(this), 'E204');
require(xIncrease != 0, 'E205');
require(yIncrease != 0, 'E205');
require(zIncrease != 0, 'E205');

Pool storage pool = pools[maturity];

if (pool.state.totalLiquidity == 0) {
uint256 liquidityTotal = MintMath.getLiquidityTotal(xIncrease);
liquidityOut = MintMath.getLiquidity(maturity, liquidityTotal, protocolFee);

pool.state.totalLiquidity += liquidityTotal;
pool.liquidities[factory.owner()] += liquidityTotal - liquidityOut;
} else {
uint256 liquidityTotal = MintMath.getLiquidityTotal(pool.state, xIncrease, yIncrease, zIncrease);
liquidityOut = MintMath.getLiquidity(maturity, liquidityTotal, protocolFee);

pool.state.totalLiquidity += liquidityTotal;
pool.liquidities[factory.owner()] += liquidityTotal - liquidityOut;
}
require(liquidityOut > 0, 'E212');
pool.liquidities[liquidityTo] += liquidityOut;

dueOut.debt = MintMath.getDebt(maturity, xIncrease, yIncrease);
dueOut.collateral = MintMath.getCollateral(maturity, zIncrease);
dueOut.startBlock = BlockNumber.get();

Callback.mint(asset, collateral, xIncrease, dueOut.collateral, data);

{ // Avoid stack too deep error
uint256 liquidityTotal = pool.state.totalLiquidity == 0 ?
MintMath.getLiquidityTotal(xIncrease) :
MintMath.getLiquidityTotal(pool.state, xIncrease, yIncrease, zIncrease);
liquidityOut = MintMath.getLiquidity(maturity, liquidityTotal, protocolFee);

pool.state.totalLiquidity += liquidityTotal;
pool.liquidities[factory.owner()] += liquidityTotal - liquidityOut;
}

require(liquidityOut != 0, 'E212');
pool.liquidities[liquidityTo] += liquidityOut;

id = pool.dues[dueTo].insert(dueOut);

pool.state.reserves.asset += xIncrease;
Expand All @@ -198,11 +194,14 @@ contract TimeswapPair is IPair {
uint256 liquidityIn
) external override lock returns (Tokens memory tokensOut) {
require(block.timestamp >= maturity, 'E203');
require(assetTo != address(0) && collateralTo != address(0), 'E201');
require(assetTo != address(this) && collateralTo != address(this), 'E204');
require(liquidityIn > 0, 'E205');
require(assetTo != address(0), 'E201');
require(collateralTo != address(0), 'E201');
require(assetTo != address(this), 'E204');
require(collateralTo != address(this), 'E204');
require(liquidityIn != 0, 'E205');

Pool storage pool = pools[maturity];
require(pool.state.totalLiquidity > 0, 'E206');

tokensOut.asset = BurnMath.getAsset(pool.state, liquidityIn);
tokensOut.collateral = BurnMath.getCollateral(pool.state, liquidityIn);
Expand All @@ -211,11 +210,14 @@ contract TimeswapPair is IPair {

pool.liquidities[msg.sender] -= liquidityIn;

pool.state.reserves.asset -= tokensOut.asset;
pool.state.reserves.collateral -= tokensOut.collateral;

if (tokensOut.asset > 0) asset.safeTransfer(assetTo, tokensOut.asset);
if (tokensOut.collateral > 0) collateral.safeTransfer(collateralTo, tokensOut.collateral);
if (tokensOut.asset != 0) {
pool.state.reserves.asset -= tokensOut.asset;
asset.safeTransfer(assetTo, tokensOut.asset);
}
if (tokensOut.collateral != 0) {
pool.state.reserves.collateral -= tokensOut.collateral;
collateral.safeTransfer(collateralTo, tokensOut.collateral);
}

emit Burn(maturity, msg.sender, assetTo, collateralTo, liquidityIn, tokensOut);
}
Expand All @@ -231,12 +233,14 @@ contract TimeswapPair is IPair {
bytes calldata data
) external override lock returns (Claims memory claimsOut) {
require(block.timestamp < maturity, 'E202');
require(bondTo != address(0) && insuranceTo != address(0), 'E201');
require(bondTo != address(this) && insuranceTo != address(this), 'E204');
require(xIncrease > 0, 'E205');
require(bondTo != address(0), 'E201');
require(insuranceTo != address(0), 'E201');
require(bondTo != address(this), 'E204');
require(insuranceTo != address(this), 'E204');
require(xIncrease != 0, 'E205');

Pool storage pool = pools[maturity];
require(pool.state.totalLiquidity > 0, 'E206');
require(pool.state.totalLiquidity != 0, 'E206');

LendMath.check(pool.state, xIncrease, yDecrease, zDecrease, fee);

Expand Down Expand Up @@ -269,9 +273,11 @@ contract TimeswapPair is IPair {
Claims memory claimsIn
) external override lock returns (Tokens memory tokensOut) {
require(block.timestamp >= maturity, 'E203');
require(assetTo != address(0) && collateralTo != address(0), 'E201');
require(assetTo != address(this) && collateralTo != address(this), 'E204');
require(claimsIn.bond > 0 || claimsIn.insurance > 0, 'E205');
require(assetTo != address(0), 'E201');
require(collateralTo != address(0), 'E201');
require(assetTo != address(this), 'E204');
require(collateralTo != address(this), 'E204');
require(claimsIn.bond != 0 || claimsIn.insurance != 0, 'E205');

Pool storage pool = pools[maturity];

Expand All @@ -286,11 +292,14 @@ contract TimeswapPair is IPair {
sender.bond -= claimsIn.bond;
sender.insurance -= claimsIn.insurance;

pool.state.reserves.asset -= tokensOut.asset;
pool.state.reserves.collateral -= tokensOut.collateral;

if (tokensOut.asset > 0) asset.safeTransfer(assetTo, tokensOut.asset);
if (tokensOut.collateral > 0) collateral.safeTransfer(collateralTo, tokensOut.collateral);
if (tokensOut.asset != 0) {
pool.state.reserves.asset -= tokensOut.asset;
asset.safeTransfer(assetTo, tokensOut.asset);
}
if (tokensOut.collateral != 0) {
pool.state.reserves.collateral -= tokensOut.collateral;
collateral.safeTransfer(collateralTo, tokensOut.collateral);
}

emit Withdraw(maturity, msg.sender, assetTo, collateralTo, claimsIn, tokensOut);
}
Expand All @@ -306,12 +315,14 @@ contract TimeswapPair is IPair {
bytes calldata data
) external override lock returns (uint256 id, Due memory dueOut) {
require(block.timestamp < maturity, 'E202');
require(assetTo != address(0) && dueTo != address(0), 'E201');
require(assetTo != address(this) && dueTo != address(this), 'E204');
require(xDecrease > 0, 'E205');
require(assetTo != address(0), 'E201');
require(dueTo != address(0), 'E201');
require(assetTo != address(this), 'E204');
require(dueTo != address(this), 'E204');
require(xDecrease != 0, 'E205');

Pool storage pool = pools[maturity];
require(pool.state.totalLiquidity > 0, 'E206');
require(pool.state.totalLiquidity != 0, 'E206');

BorrowMath.check(pool.state, xDecrease, yIncrease, zIncrease, fee);

Expand Down Expand Up @@ -348,30 +359,35 @@ contract TimeswapPair is IPair {
bytes calldata data
) external override lock returns (uint128 assetIn, uint128 collateralOut) {
require(block.timestamp < maturity, 'E202');
require(ids.length == assetsIn.length && ids.length == collateralsOut.length, 'E205');
require(owner != address(0), 'E201');
require(to != address(0), 'E201');
require(to != address(this), 'E204');
require(ids.length == assetsIn.length, 'E205');
require(ids.length == collateralsOut.length, 'E205');

Pool storage pool = pools[maturity];

Due[] storage dues = pool.dues[owner];
require(dues.length >= ids.length, 'E205');

for (uint256 i; i < ids.length; i++) {
for (uint256 i; i < ids.length;) {
Due storage due = dues[ids[i]];
require(due.startBlock != BlockNumber.get(), 'E207');
if (owner != msg.sender) require(collateralsOut[i] == 0, 'E213');
PayMath.checkProportional(assetsIn[i], collateralsOut[i], due);
require(uint256(assetIn) * due.collateral >= uint256(collateralOut) * due.debt, 'E303');
due.debt -= assetsIn[i];
due.collateral -= collateralsOut[i];
assetIn += assetsIn[i];
collateralOut += collateralsOut[i];
unchecked { ++i; }
}
if (assetIn > 0) Callback.pay(asset, assetIn, data);

pool.state.reserves.asset += assetIn;
pool.state.reserves.collateral -= collateralOut;

if (collateralOut > 0) collateral.safeTransfer(to, collateralOut);
if (collateralOut != 0) collateral.safeTransfer(to, collateralOut);

if (assetIn != 0) Callback.pay(asset, assetIn, data);

emit Pay(maturity, msg.sender, to, owner, ids, assetsIn, collateralsOut, assetIn, collateralOut);
}
Expand Down
3 changes: 2 additions & 1 deletion contracts/interfaces/IFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ interface IFactory {
function fee() external view returns (uint16);

/// @dev Return the protocol fee per second earned by the owner.
/// @return The protocol fee per seconde following UQ0.40 format.
/// @return The protocol fee per second following UQ0.40 format.
function protocolFee() external view returns (uint16);

/// @dev Returns the address of a deployed pair.
Expand All @@ -60,6 +60,7 @@ interface IFactory {
function setOwner(address _pendingOwner) external;

/// @dev Set the pending owner as the owner of the factory.
/// @dev Reset the pending owner to zero.
/// @dev Can only be called by the pending owner.
function acceptOwner() external;
}
4 changes: 2 additions & 2 deletions contracts/interfaces/IPair.sol
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,8 @@ interface IPair {
/// @dev Must be called by a contract implementing the ITimeswapBorrowCallback interface.
/// @param maturity The unix timestamp maturity of the Pool.
/// @param assetTo The address of the receiver of asset ERC20.
/// @param dueTo The addres of the receiver of collateralized debt.
/// @param xDecrease The dcrease in x state and amount of asset ERC20 received by assetTo.
/// @param dueTo The address of the receiver of collateralized debt.
/// @param xDecrease The decrease in x state and amount of asset ERC20 received by assetTo.
/// @param yIncrease The increase in y state.
/// @param zIncrease The increase in z state.
/// @param data The data for callback.
Expand Down
4 changes: 2 additions & 2 deletions contracts/libraries/FullMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ library FullMath {

// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
require(denominator > 0);
require(denominator != 0);
assembly {
result := div(prod0, denominator)
}
Expand Down Expand Up @@ -121,6 +121,6 @@ library FullMath {
uint256 denominator
) internal pure returns (uint256 result) {
result = mulDiv(a, b, denominator);
if (mulmod(a, b, denominator) > 0) result++;
if (mulmod(a, b, denominator) != 0) result++;
}
}
2 changes: 1 addition & 1 deletion contracts/libraries/Math.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity =0.8.4;
library Math {
function divUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x / y;
if (x % y > 0) z++;
if (x % y != 0) z++;
}

function shiftRightUp(uint256 x, uint8 y) internal pure returns (uint256 z) {
Expand Down
2 changes: 1 addition & 1 deletion contracts/libraries/MintMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ library MintMath {
) private pure returns (uint256 w) {
if (x <= y && x <= z) {
w = x;
} else if (y <= x && y <= z) {
} else if (y <= z) {
w = y;
} else {
w = z;
Expand Down
14 changes: 0 additions & 14 deletions contracts/libraries/PayMath.sol

This file was deleted.

6 changes: 4 additions & 2 deletions contracts/libraries/SafeCast.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ library SafeCast {
}

function toUint112(uint256 x) internal pure returns (uint112 y) {
require((y = uint112(x)) == x);
require(x <= type(uint112).max);
y = uint112(x);
}

function toUint128(uint256 x) internal pure returns (uint128 y) {
require((y = uint128(x)) == x);
require(x <= type(uint128).max);
y = uint128(x);
}

function truncateUint112(uint256 x) internal pure returns (uint112 y) {
Expand Down
17 changes: 0 additions & 17 deletions contracts/libraries/SafeTransfer.sol

This file was deleted.

Loading