clean up names and implementation of oracle stuff
update UQ104.104 copy make safetransfer much clearer
This commit is contained in:
@ -84,31 +84,33 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTran
|
||||
return (reserves.token0, reserves.token1);
|
||||
}
|
||||
|
||||
function getBlockNumberLast() public view returns (uint32) {
|
||||
return oracleDataToken0.blockNumberHalf + (uint32(oracleDataToken1.blockNumberHalf) << 16);
|
||||
function readOraclePricesAccumulated() external view returns (uint240, uint240) {
|
||||
return (oracleDataToken0.priceAccumulated, oracleDataToken1.priceAccumulated);
|
||||
}
|
||||
|
||||
function getAccumulatedPrices() external view returns (uint256, uint256) {
|
||||
uint32 blockNumberLast = getBlockNumberLast();
|
||||
function readOracleBlockNumber() public view returns (uint32) {
|
||||
return (uint32(oracleDataToken0.blockNumberHalf) << 16) + oracleDataToken1.blockNumberHalf;
|
||||
}
|
||||
|
||||
require(blockNumberLast > 0, "UniswapV2: NOT_INITIALIZED");
|
||||
function consultOracle() external view returns (uint240, uint240) {
|
||||
uint32 blockNumberLast = readOracleBlockNumber();
|
||||
|
||||
require(reserves.token0 != 0 && reserves.token1 != 0, "UniswapV2: NO_LIQUIDITY");
|
||||
|
||||
// replicate the logic in update
|
||||
if (block.number > blockNumberLast) {
|
||||
uint128 blocksElapsed = (block.number - blockNumberLast).downcast128();
|
||||
|
||||
// get the prices according to the reserves as of the last official interaction with the contract
|
||||
uint240 priceToken0 = UQ104x104.encode(reserves.token0).qdiv(reserves.token1);
|
||||
uint240 priceToken1 = UQ104x104.encode(reserves.token1).qdiv(reserves.token0);
|
||||
|
||||
uint32 blocksElapsed = block.number.downcast32() - blockNumberLast;
|
||||
return (
|
||||
oracleDataToken0.priceAccumulated + priceToken0 * blocksElapsed,
|
||||
oracleDataToken0.priceAccumulated + priceToken1 * blocksElapsed
|
||||
oracleDataToken1.priceAccumulated + priceToken1 * blocksElapsed
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
oracleDataToken0.priceAccumulated,
|
||||
oracleDataToken0.priceAccumulated
|
||||
oracleDataToken1.priceAccumulated
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -118,34 +120,32 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTran
|
||||
public pure returns (uint128 amountOutput)
|
||||
{
|
||||
require(amountInput > 0 && reserveInput > 0 && reserveOutput > 0, "UniswapV2: INVALID_VALUE");
|
||||
uint256 amountInputWithFee = uint256(amountInput).mul(1000 - 3);
|
||||
uint256 amountInputWithFee = uint256(amountInput).mul(997);
|
||||
uint256 numerator = amountInputWithFee.mul(reserveOutput);
|
||||
uint256 denominator = uint256(reserveInput).mul(1000).add(amountInputWithFee);
|
||||
amountOutput = (numerator / denominator).downcast128();
|
||||
}
|
||||
|
||||
function update(TokenData memory balances) private {
|
||||
uint32 blockNumberLast = getBlockNumberLast();
|
||||
uint32 blockNumberLast = readOracleBlockNumber();
|
||||
|
||||
// if any blocks have gone by since the last time this function was called, we have to update
|
||||
if (block.number > blockNumberLast) {
|
||||
// make sure that this isn't the first time this function is being called
|
||||
uint32 blockNumber = block.number.downcast32();
|
||||
if (blockNumberLast > 0) {
|
||||
uint32 blocksElapsed = blockNumber - blockNumberLast;
|
||||
|
||||
// we have to ensure that neither reserves are 0, else our price division fails
|
||||
if (reserves.token0 != 0 && reserves.token1 != 0) {
|
||||
// get the prices according to the reserves as of the last official interaction with the contract
|
||||
uint240 priceToken0 = UQ104x104.encode(reserves.token0).qdiv(reserves.token1);
|
||||
uint240 priceToken1 = UQ104x104.encode(reserves.token1).qdiv(reserves.token0);
|
||||
|
||||
// multiply these prices by the number of elapsed blocks and add to the accumulators
|
||||
oracleDataToken0.priceAccumulated = oracleDataToken0.priceAccumulated + priceToken0 * blocksElapsed;
|
||||
oracleDataToken1.priceAccumulated = oracleDataToken1.priceAccumulated + priceToken1 * blocksElapsed;
|
||||
uint32 blocksElapsed = block.number.downcast32() - blockNumberLast;
|
||||
oracleDataToken0.priceAccumulated += priceToken0 * blocksElapsed;
|
||||
oracleDataToken1.priceAccumulated += priceToken1 * blocksElapsed;
|
||||
}
|
||||
|
||||
// update the last block number
|
||||
oracleDataToken0.blockNumberHalf = uint16(now);
|
||||
oracleDataToken1.blockNumberHalf = uint16(uint32(now >> 16));
|
||||
oracleDataToken0.blockNumberHalf = uint16(block.number >> 16);
|
||||
oracleDataToken1.blockNumberHalf = uint16(block.number);
|
||||
}
|
||||
|
||||
// update reserves
|
||||
|
||||
@ -10,20 +10,20 @@ library UQ104x104 {
|
||||
|
||||
// we want to encode a uint128 `y` s.t. `y := y_encoded / 2**104` (i.e. with a Q104 denominator).
|
||||
// in other words, to encode `y` we simply multiply by `2**104`, aka Q104.
|
||||
// however, in the case of a traditional UQ104.104, we'd store this output in a 208-bit slot,
|
||||
// which would overflow for values of `y` in (`uint104(-1)`, `uint128(-1)`], so instead we need to
|
||||
// store the output in 232 bits (TODO check this logic).
|
||||
// in the case of a traditional UQ104.104, we'd store this output in a 208-bit slot,
|
||||
// but since we're encoding a uint128, this would overflow for values of `y` in (`uint104(-1)`, `uint128(-1)`],
|
||||
// so instead we need to store the output in at least 232 bits (we use 240 for compatibility later on)
|
||||
function encode(uint128 y) internal pure returns (uint240 z) {
|
||||
return uint240(y) * Q104;
|
||||
}
|
||||
|
||||
// we want to divide a modified UQ104.104 (the output of encode) by an unencoded uint128,
|
||||
// and return a traditional Q104. since we're using a modified UQ104.104, though, we need to handle overflows.
|
||||
// for the moment, we simply truncate these to 1 and uint208(-1), though it's likely we'll handle this slightly
|
||||
// differently in the future
|
||||
// we want to divide a modified UQ104.104 (the output of encode above) by an unencoded uint128 and return another
|
||||
// modified UQ104.104. to do this, it's sufficient to divide the UQ104.104 by the unencoded value.
|
||||
// since we want our output to fit in 208 bits, and behave consistently at the margins, we clamp this quotient
|
||||
// within [1, uint208(-1)]
|
||||
function qdiv(uint240 x, uint128 y) internal pure returns (uint240 z) {
|
||||
z = x / y;
|
||||
|
||||
|
||||
if (z == 0) {
|
||||
z = 1;
|
||||
} else if (z > uint208(-1)) {
|
||||
|
||||
@ -1,28 +1,15 @@
|
||||
pragma solidity 0.5.12;
|
||||
|
||||
interface IIncompatibleERC20 {
|
||||
function transfer(address to, uint256 value) external;
|
||||
}
|
||||
|
||||
contract SafeTransfer {
|
||||
function safeTransfer(address token, address to, uint256 value) internal {
|
||||
IIncompatibleERC20(token).transfer(to, value);
|
||||
(bool success, bytes memory data) = token.call(abi.encodeWithSignature("transfer(address,uint256)", to, value));
|
||||
|
||||
bool result;
|
||||
assembly {
|
||||
switch returndatasize()
|
||||
case 0 { // if there was no return data, treat the transfer as successful
|
||||
result := 1
|
||||
}
|
||||
case 0x20 { // if the return data was 32 bytes long, return that value
|
||||
returndatacopy(0, 0, 0x20)
|
||||
result := mload(0)
|
||||
}
|
||||
default { // revert in all other cases
|
||||
revert(0, 0)
|
||||
}
|
||||
require(success, "SafeTransfer: SWAP_FAILED");
|
||||
|
||||
if (data.length == 32) {
|
||||
require(abi.decode(data, (bool)), "SafeTransfer: SWAP_FAILED");
|
||||
} else if (data.length > 32) {
|
||||
revert("SafeTransfer: SWAP_FAILED");
|
||||
}
|
||||
|
||||
require(result, "SafeTransfer: SWAP_FAILED");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user