// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address to, uint256 amount) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom,
address to,
uint256 amount
) externalreturns (bool);
}
// SPDX-License-Identifier: MITpragmasolidity 0.8.18;interfaceIUniswapV2Factory{
structDefaultFees {
/// @dev in basis point, denominated by 10000uint256 protocolFee;
/// @dev in basis point, denominated by 10000uint256 lpFee;
}
structFees {
address royaltiesBeneficiary;
/// @dev in basis point, denominated by 10000uint256 royaltiesFee;
/// @dev in basis point, denominated by 10000uint256 protocolFee;
/// @dev in basis point, denominated by 10000uint256 lpFee;
/// @dev if true, Fees.protocolFee is used even if set to 0bool protocolFeeOverride;
/// @dev if true, Fees.lpFee is used even if set to 0bool lpFeeOverride;
}
eventPairCreated(addressindexed token0, addressindexed token1, address pair, uint256);
eventDefaultFeesSet(DefaultFees fees);
eventLpFeesSet(addressindexed pair, uint256 lpFee, bool overrideFee);
eventRoyaltiesFeesSet(addressindexed pair, address beneficiary, uint256 royaltiesFee);
eventProtocolFeesSet(addressindexed pair, uint256 protocolFee, bool overrideFee);
eventProtocolFeeBeneficiarySet(address beneficiary);
/// @notice Returns total fee pair charges/// @dev Fee is capped at MAX_FEE/// @param pair address of pair for which to calculate fees/// @return totalFee total fee amount denominated in basis pointsfunctiongetTotalFee(address pair) externalviewreturns (uint256 totalFee);
/// @notice Returns all fees for pair/// @return lpFee fee changed by liquidity providers, denominated in basis points/// @return royaltiesFee royalties paid to NFT creators, denominated in basis points/// @return protocolFee fee paid to the protocol, denominated in basis pointsfunctiongetFees(address _pair) externalviewreturns (uint256 lpFee, uint256 royaltiesFee, uint256 protocolFee);
/// @notice Returns all fees for pair and beneficiaries/// @dev Fees are capped in total by MAX_FEE value. If by mistake or otherwise owner of this contract/// does a combination of transactions and tries to achive total fees above MAX_FEE, fees are allocatied/// by priority:/// 1. lp fee/// 2. royalties/// 3. protocol fee/// If MAX_FEE == 5000, lpFee == 500, royaltiesFee == 4000 and protocolFee == 4000 then/// effective fees will be allocated acording to the fee priority up to MAX_FEE value./// In this example: lpFee == 500, royaltiesFee == 4000 and protocolFee == 500./// @param pair address of pair for which to calculate fees and beneficiaries/// @return lpFee fee changed by liquidity providers, denominated in basis points/// @return royaltiesBeneficiary address that gets royalties/// @return royaltiesFee royalties paid to NFT creators, denominated in basis points/// @return protocolBeneficiary address that gets protocol fees/// @return protocolFee fee paid to the protocol, denominated in basis pointsfunctiongetFeesAndRecipients(address pair)
externalviewreturns (uint256 lpFee,
address royaltiesBeneficiary,
uint256 royaltiesFee,
address protocolBeneficiary,
uint256 protocolFee
);
/// @return protocolFeeBeneficiary address that gets protocol feesfunctionprotocolFeeBeneficiary() externalviewreturns (address protocolFeeBeneficiary);
/// @notice Internal mapping to store fees for pair. It is exposed for advanced integrations/// and in most cases contracts should use fee getters.functionpairFees(address pair) externalviewreturns (address, uint256, uint256, uint256, bool, bool);
functiongetPair(address tokenA, address tokenB) externalviewreturns (address pair);
functionallPairs() externalviewreturns (address[] memory pairs);
functionallPairs(uint256) externalviewreturns (address pair);
functionallPairsLength() externalviewreturns (uint256);
functioncreatePair(address tokenA, address tokenB) externalreturns (address pair);
/// @notice Sets default fees for all pairs/// @param fees struct with default feesfunctionsetDefaultFees(DefaultFees memory fees) external;
/// @notice Sets royalties fee and beneficiary for pair/// @param pair address of pair for which to set fee/// @param beneficiary address that gets royalties/// @param royaltiesFee amount of royalties fee denominated in basis pointsfunctionsetRoyaltiesFee(address pair, address beneficiary, uint256 royaltiesFee) external;
/// @notice Sets protocol fee for pair/// @param pair address of pair for which to set fee/// @param protocolFee amount of protocol fee denominated in basis points/// @param overrideFee if true, fee will be overriden even if set to 0functionsetProtocolFee(address pair, uint256 protocolFee, bool overrideFee) external;
/// @notice Sets lp fee for pair/// @param pair address of pair for which to set fee/// @param lpFee amount of lp fee denominated in basis points/// @param overrideFee if true, fee will be overriden even if set to 0functionsetLpFee(address pair, uint256 lpFee, bool overrideFee) external;
/// @notice Sets protocol fee beneficiary/// @param _beneficiary address that gets protocol feesfunctionsetProtocolFeeBeneficiary(address _beneficiary) external;
}
Contract Source Code
File 4 of 9: IUniswapV2Pair.sol
// SPDX-License-Identifier: MITpragmasolidity 0.8.18;interfaceIUniswapV2Pair{
eventMint(addressindexed sender, uint256 amount0, uint256 amount1);
eventBurn(addressindexed sender, uint256 amount0, uint256 amount1, addressindexed to);
eventSwap(addressindexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
addressindexed to
);
eventSync(uint112 reserve0, uint112 reserve1);
/// @notice Emitted by the pool for increases to the number of observations that can be stored/// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index/// just before a mint/swap/burn./// @param observationCardinalityNextOld The previous value of the next observation cardinality/// @param observationCardinalityNextNew The updated value of the next observation cardinalityeventIncreaseObservationCardinalityNext(uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew
);
functionMINIMUM_LIQUIDITY() externalpurereturns (uint256);
functionfactory() externalviewreturns (address);
functiontoken0() externalviewreturns (address);
functiontoken1() externalviewreturns (address);
functiongetReserves() externalviewreturns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
functionobservations(uint256 index)
externalviewreturns (uint32 blockTimestamp, uint256 priceCumulative, bool initialized);
functionobserve(uint32[] calldata secondsAgos) externalviewreturns (uint256[] memory tickCumulatives);
functionincreaseObservationCardinalityNext(uint16 observationCardinalityNext) external;
functionmint(address to) externalreturns (uint256 liquidity);
functionburn(address to) externalreturns (uint256 amount0, uint256 amount1);
functionswap(uint256 amount0Out, uint256 amount1Out, address to, bytescalldata data) external;
functionskim(address to) external;
functionsync() external;
functioninitialize(address, address) external;
}
Contract Source Code
File 5 of 9: Oracle.sol
// SPDX-License-Identifier: MITpragmasolidity 0.8.18;/// @title Oracle (modifier version of Oracle.sol from UniswapV3)/// @notice Provides price data useful for a wide variety of system designs/// @dev Instances of stored oracle data, "observations", are collected in the oracle array/// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the/// maximum length of the oracle array. New slots will be added when the array is fully populated./// Observations are overwritten when the full length of the oracle array is populated./// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe()libraryOracle{
structObservation {
// the block timestamp of the observationuint32 blockTimestamp;
// the price accumulator, i.e. price * time elapsed since the pool was first initializeduint256 priceCumulative;
// whether or not the observation is initializedbool initialized;
}
/// @notice Transforms a previous observation into a new observation, given the passage of time and the current price values/// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows/// @param last The specified observation to be transformed/// @param blockTimestamp The timestamp of the new observation/// @param lastPrice The active price at the time of the new observation/// @return Observation The newly populated observationfunctiontransform(Observation memory last, uint32 blockTimestamp, uint256 lastPrice)
privatepurereturns (Observation memory)
{
uint32 delta = blockTimestamp - last.blockTimestamp;
return Observation({
blockTimestamp: blockTimestamp,
priceCumulative: last.priceCumulative + lastPrice * delta,
initialized: true
});
}
/// @notice Initialize the oracle array by writing the first slot. Called once for the lifecycle of the observations array/// @param self The stored oracle array/// @param time The time of the oracle initialization, via block.timestamp truncated to uint32/// @return cardinality The number of populated elements in the oracle array/// @return cardinalityNext The new length of the oracle array, independent of populationfunctioninitialize(Observation[65535] storageself, uint32 time)
internalreturns (uint16 cardinality, uint16 cardinalityNext)
{
self[0] = Observation({blockTimestamp: time, priceCumulative: 0, initialized: true});
return (1, 1);
}
/// @notice Writes an oracle observation to the array/// @dev Writable at most once per block. Index represents the most recently written element. cardinality and index must be tracked externally./// If the index is at the end of the allowable array length (according to cardinality), and the next cardinality/// is greater than the current one, cardinality may be increased. This restriction is created to preserve ordering./// @param self The stored oracle array/// @param index The index of the observation that was most recently written to the observations array/// @param blockTimestamp The timestamp of the new observation/// @param lastPrice The active price at the time of the new observation/// @param cardinality The number of populated elements in the oracle array/// @param cardinalityNext The new length of the oracle array, independent of population/// @return indexUpdated The new index of the most recently written element in the oracle array/// @return cardinalityUpdated The new cardinality of the oracle arrayfunctionwrite(
Observation[65535] storageself,
uint16 index,
uint32 blockTimestamp,
uint256 lastPrice,
uint16 cardinality,
uint16 cardinalityNext
) internalreturns (uint16 indexUpdated, uint16 cardinalityUpdated) {
Observation memory last =self[index];
// early return if we've already written an observation this blockif (last.blockTimestamp == blockTimestamp) return (index, cardinality);
// if the conditions are right, we can bump the cardinalityif (cardinalityNext > cardinality && index == (cardinality -1)) {
cardinalityUpdated = cardinalityNext;
} else {
cardinalityUpdated = cardinality;
}
indexUpdated = (index +1) % cardinalityUpdated;
self[indexUpdated] = transform(last, blockTimestamp, lastPrice);
}
/// @notice Prepares the oracle array to store up to `next` observations/// @param self The stored oracle array/// @param current The current next cardinality of the oracle array/// @param next The proposed next cardinality which will be populated in the oracle array/// @return next The next cardinality which will be populated in the oracle arrayfunctiongrow(Observation[65535] storageself, uint16 current, uint16 next) internalreturns (uint16) {
require(current >0, "I");
// no-op if the passed next value isn't greater than the current next valueif (next <= current) return current;
// store in each slot to prevent fresh SSTOREs in swaps// this data will not be used because the initialized boolean is still falsefor (uint16 i = current; i < next; i++) {
self[i].blockTimestamp =1;
}
return next;
}
/// @notice comparator for 32-bit timestamps/// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to time/// @param time A timestamp truncated to 32 bits/// @param a A comparison timestamp from which to determine the relative position of `time`/// @param b From which to determine the relative position of `time`/// @return bool Whether `a` is chronologically <= `b`functionlte(uint32 time, uint32 a, uint32 b) privatepurereturns (bool) {
// if there hasn't been overflow, no need to adjustif (a <= time && b <= time) return a <= b;
uint256 aAdjusted = a > time ? a : a +2**32;
uint256 bAdjusted = b > time ? b : b +2**32;
return aAdjusted <= bAdjusted;
}
/// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied./// The result may be the same observation, or adjacent observations./// @dev The answer must be contained in the array, used when the target is located within the stored observation/// boundaries: older than the most recent observation and younger, or the same age as, the oldest observation/// @param self The stored oracle array/// @param time The current block.timestamp/// @param target The timestamp at which the reserved observation should be for/// @param index The index of the observation that was most recently written to the observations array/// @param cardinality The number of populated elements in the oracle array/// @return beforeOrAt The observation recorded before, or at, the target/// @return atOrAfter The observation recorded at, or after, the targetfunctionbinarySearch(Observation[65535] storageself, uint32 time, uint32 target, uint16 index, uint16 cardinality)
privateviewreturns (Observation memory beforeOrAt, Observation memory atOrAfter)
{
uint256 l = (index +1) % cardinality; // oldest observationuint256 r = l + cardinality -1; // newest observationuint256 i;
while (true) {
i = (l + r) /2;
beforeOrAt =self[i % cardinality];
// we've landed on an uninitialized price, keep searching higher (more recently)if (!beforeOrAt.initialized) {
l = i +1;
continue;
}
atOrAfter =self[(i +1) % cardinality];
bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target);
// check if we've found the answer!if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break;
if (!targetAtOrAfter) r = i -1;
else l = i +1;
}
}
/// @notice Fetches the observations beforeOrAt and atOrAfter a given target, i.e. where [beforeOrAt, atOrAfter] is satisfied/// @dev Assumes there is at least 1 initialized observation./// Used by observeSingle() to compute the counterfactual accumulator values as of a given block timestamp./// @param self The stored oracle array/// @param time The current block.timestamp/// @param target The timestamp at which the reserved observation should be for/// @param lastPrice The active price at the time of the returned or simulated observation/// @param index The index of the observation that was most recently written to the observations array/// @param cardinality The number of populated elements in the oracle array/// @return beforeOrAt The observation which occurred at, or before, the given timestamp/// @return atOrAfter The observation which occurred at, or after, the given timestampfunctiongetSurroundingObservations(
Observation[65535] storageself,
uint32 time,
uint32 target,
uint256 lastPrice,
uint16 index,
uint16 cardinality
) privateviewreturns (Observation memory beforeOrAt, Observation memory atOrAfter) {
// optimistically set before to the newest observation
beforeOrAt =self[index];
// if the target is chronologically at or after the newest observation, we can early returnif (lte(time, beforeOrAt.blockTimestamp, target)) {
if (beforeOrAt.blockTimestamp == target) {
// if newest observation equals target, we're in the same block, so we can ignore atOrAfterreturn (beforeOrAt, atOrAfter);
} else {
// otherwise, we need to transformreturn (beforeOrAt, transform(beforeOrAt, target, lastPrice));
}
}
// now, set before to the oldest observation
beforeOrAt =self[(index +1) % cardinality];
if (!beforeOrAt.initialized) beforeOrAt =self[0];
// ensure that the target is chronologically at or after the oldest observationrequire(lte(time, beforeOrAt.blockTimestamp, target), "OLD");
// if we've reached this point, we have to binary searchreturn binarySearch(self, time, target, index, cardinality);
}
/// @dev Reverts if an observation at or before the desired observation timestamp does not exist./// 0 may be passed as `secondsAgo' to return the current cumulative values./// If called with a timestamp falling between two observations, returns the counterfactual accumulator values/// at exactly the timestamp between the two observations./// @param self The stored oracle array/// @param time The current block timestamp/// @param secondsAgo The amount of time to look back, in seconds, at which point to return an observation/// @param lastPrice The current price/// @param index The index of the observation that was most recently written to the observations array/// @param cardinality The number of populated elements in the oracle array/// @return priceCumulative The price * time elapsed since the pool was first initialized, as of `secondsAgo`functionobserveSingle(
Observation[65535] storageself,
uint32 time,
uint32 secondsAgo,
uint256 lastPrice,
uint16 index,
uint16 cardinality
) internalviewreturns (uint256 priceCumulative) {
if (secondsAgo ==0) {
Observation memory last =self[index];
if (last.blockTimestamp != time) last = transform(last, time, lastPrice);
return last.priceCumulative;
}
uint32 target = time - secondsAgo;
(Observation memory beforeOrAt, Observation memory atOrAfter) =
getSurroundingObservations(self, time, target, lastPrice, index, cardinality);
if (target == beforeOrAt.blockTimestamp) {
// we're at the left boundaryreturn beforeOrAt.priceCumulative;
} elseif (target == atOrAfter.blockTimestamp) {
// we're at the right boundaryreturn atOrAfter.priceCumulative;
} else {
// we're in the middleuint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp;
uint32 targetDelta = target - beforeOrAt.blockTimestamp;
return beforeOrAt.priceCumulative
+ ((atOrAfter.priceCumulative - beforeOrAt.priceCumulative) / observationTimeDelta) * targetDelta;
}
}
/// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos`/// @dev Reverts if `secondsAgos` > oldest observation/// @param self The stored oracle array/// @param time The current block.timestamp/// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an observation/// @param lastPrice The current price/// @param index The index of the observation that was most recently written to the observations array/// @param cardinality The number of populated elements in the oracle array/// @return priceCumulatives The price * time elapsed since the pool was first initialized, as of each `secondsAgo`functionobserve(
Observation[65535] storageself,
uint32 time,
uint32[] memory secondsAgos,
uint256 lastPrice,
uint16 index,
uint16 cardinality
) internalviewreturns (uint256[] memory priceCumulatives) {
require(cardinality >0, "I");
priceCumulatives =newuint256[](secondsAgos.length);
for (uint256 i =0; i < secondsAgos.length; i++) {
priceCumulatives[i] = observeSingle(self, time, secondsAgos[i], lastPrice, index, cardinality);
}
}
}
Contract Source Code
File 6 of 9: SafeMath.sol
// SPDX-License-Identifier: MITpragmasolidity 0.8.18;// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)librarySafeMath{
functionadd(uint256 x, uint256 y) internalpurereturns (uint256 z) {
require((z = x + y) >= x, "ds-math-add-overflow");
}
functionsub(uint256 x, uint256 y) internalpurereturns (uint256 z) {
require((z = x - y) <= x, "ds-math-sub-underflow");
}
functionmul(uint256 x, uint256 y) internalpurereturns (uint256 z) {
require(y ==0|| (z = x * y) / y == x, "ds-math-mul-overflow");
}
}
// SPDX-License-Identifier: MITpragmasolidity 0.8.18;// a library for performing various math operationslibraryUniswapV2Math{
functionmin(uint256 x, uint256 y) internalpurereturns (uint256 z) {
z = x < y ? x : y;
}
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)functionsqrt(uint256 y) internalpurereturns (uint256 z) {
if (y >3) {
z = y;
uint256 x = y /2+1;
while (x < z) {
z = x;
x = (y / x + x) /2;
}
} elseif (y !=0) {
z =1;
}
}
}
Contract Source Code
File 9 of 9: UniswapV2Pair.sol
// SPDX-License-Identifier: MITpragmasolidity 0.8.18;import"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import"./interfaces/IUniswapV2Pair.sol";
import"./interfaces/IUniswapV2Factory.sol";
import"./libraries/UniswapV2Math.sol";
import"./libraries/Oracle.sol";
import"./UniswapV2ERC20.sol";
contractUniswapV2PairisIUniswapV2Pair, UniswapV2ERC20{
usingSafeMathforuint256;
usingOracleforOracle.Observation[65535];
uint256publicconstant MINIMUM_LIQUIDITY =10**3;
uint256publicconstant BASIS_POINTS =10000;
bytes4privateconstant SELECTOR =bytes4(keccak256(bytes("transfer(address,uint256)")));
// decimal points of token0uint256public TOKEN0_DECIMALS;
addresspublic factory;
addresspublic token0;
addresspublic token1;
uint112private reserve0; // uses single storage slot, accessible via getReservesuint112private reserve1; // uses single storage slot, accessible via getReservesuint32private blockTimestampLast; // uses single storage slot, accessible via getReserves// the most recent price of token1/token0. Inherits decimals of token1.uint256public lastPrice;
// the most-recently updated index of the observations arrayuint16public observationIndex;
// the current maximum number of observations that are being storeduint16public observationCardinality;
// the next maximum number of observations to store, triggered in observations.writeuint16public observationCardinalityNext;
Oracle.Observation[65535] publicoverride observations;
uint256private unlocked =1;
modifierlock() {
require(unlocked ==1, "MagicswapV2: LOCKED");
unlocked =0;
_;
unlocked =1;
}
functiongetReserves() publicviewreturns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
_reserve0 = reserve0;
_reserve1 = reserve1;
_blockTimestampLast = blockTimestampLast;
}
function_safeTransfer(address token, address to, uint256 value) private{
(bool success, bytesmemory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
require(success && (data.length==0||abi.decode(data, (bool))), "MagicswapV2: TRANSFER_FAILED");
}
constructor() {
factory =msg.sender;
}
// called once by the factory at time of deploymentfunctioninitialize(address _token0, address _token1) external{
require(msg.sender== factory, "MagicswapV2: FORBIDDEN"); // sufficient check
token0 = _token0;
token1 = _token1;
TOKEN0_DECIMALS = UniswapV2ERC20(_token0).decimals();
(uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp());
observationIndex =0;
observationCardinality = cardinality;
observationCardinalityNext = cardinalityNext;
}
function_blockTimestamp() internalviewvirtualreturns (uint32) {
returnuint32(block.timestamp); // truncation is desired
}
/// @dev update reserves and, on the first call per block, price accumulatorsfunction_update(uint256 balance0, uint256 balance1, uint112 _reserve0, uint112 _reserve1) private{
require(balance0 <=type(uint112).max&& balance1 <=type(uint112).max, "MagicswapV2: OVERFLOW");
uint32 blockTimestamp;
uint32 timeElapsed;
unchecked {
blockTimestamp =uint32(block.timestamp%2**32);
timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
}
if (timeElapsed >0&& _reserve0 !=0&& _reserve1 !=0) {
// this is first trade of the block and reserves are not yet updated
lastPrice =10** TOKEN0_DECIMALS * _reserve1 / _reserve0;
// write an oracle entry
(observationIndex, observationCardinality) = observations.write(
observationIndex, _blockTimestamp(), lastPrice, observationCardinality, observationCardinalityNext
);
}
reserve0 =uint112(balance0);
reserve1 =uint112(balance1);
blockTimestampLast = blockTimestamp;
emit Sync(reserve0, reserve1);
}
/// @dev Calculates fees and sends them to beneficiariesfunction_takeFees(uint256 balance0Adjusted, uint256 balance1Adjusted, uint256 amount0In, uint256 amount1In)
internalreturns (uint256 balance0, uint256 balance1)
{
(, address royaltiesBeneficiary, uint256 royaltiesFee, address protocolFeeBeneficiary, uint256 protocolFee) =
IUniswapV2Factory(factory).getFeesAndRecipients(address(this));
address _token0 = token0;
address _token1 = token1;
for (uint8 i =0; i <2; i++) {
address feeToken = i ==0 ? _token0 : _token1;
uint256 swapAmount = i ==0 ? amount0In : amount1In;
if (swapAmount >0) {
uint256 royaltiesFeeAmount = swapAmount * royaltiesFee / BASIS_POINTS;
// send royaltiesif (royaltiesFeeAmount >0) {
_safeTransfer(feeToken, royaltiesBeneficiary, royaltiesFeeAmount);
}
uint256 protocolFeeAmount = swapAmount * protocolFee / BASIS_POINTS;
// send protocol feeif (protocolFeeAmount >0) {
_safeTransfer(feeToken, protocolFeeBeneficiary, protocolFeeAmount);
}
}
}
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
// Make sure that either balance does not go below adjusted balance used for K calcualtions.// If balances after fee transfers are above or equal adjusted balances then K still holds.require(balance0 >= balance0Adjusted / BASIS_POINTS, "MagicswapV2: balance0Adjusted");
require(balance1 >= balance1Adjusted / BASIS_POINTS, "MagicswapV2: balance1Adjusted");
}
/// @dev this low-level function should be called from a contract which performs important safety checksfunctionmint(address to) externallockreturns (uint256 liquidity) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savingsuint256 balance0 = IERC20(token0).balanceOf(address(this));
uint256 balance1 = IERC20(token1).balanceOf(address(this));
uint256 amount0 = balance0.sub(_reserve0);
uint256 amount1 = balance1.sub(_reserve1);
uint256 _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFeeif (_totalSupply ==0) {
liquidity = UniswapV2Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
_mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
} else {
liquidity = UniswapV2Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
}
require(liquidity >0, "MagicswapV2: INSUFFICIENT_LIQUIDITY_MINTED");
_mint(to, liquidity);
_update(balance0, balance1, _reserve0, _reserve1);
emit Mint(msg.sender, amount0, amount1);
}
/// @dev this low-level function should be called from a contract which performs important safety checksfunctionburn(address to) externallockreturns (uint256 amount0, uint256 amount1) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savingsaddress _token0 = token0; // gas savingsaddress _token1 = token1; // gas savingsuint256 balance0 = IERC20(_token0).balanceOf(address(this));
uint256 balance1 = IERC20(_token1).balanceOf(address(this));
uint256 liquidity = balanceOf[address(this)];
uint256 _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distributionrequire(amount0 >0&& amount1 >0, "MagicswapV2: INSUFFICIENT_LIQUIDITY_BURNED");
_burn(address(this), liquidity);
_safeTransfer(_token0, to, amount0);
_safeTransfer(_token1, to, amount1);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
_update(balance0, balance1, _reserve0, _reserve1);
emit Burn(msg.sender, amount0, amount1, to);
}
/// @dev this low-level function should be called from a contract which performs important safety checks/// @dev keeping bytes parameter for backward compatibility of the interfacefunctionswap(uint256 amount0Out, uint256 amount1Out, address to, bytescalldata) externallock{
require(amount0Out >0|| amount1Out >0, "MagicswapV2: INSUFFICIENT_OUTPUT_AMOUNT");
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savingsrequire(amount0Out < _reserve0 && amount1Out < _reserve1, "MagicswapV2: INSUFFICIENT_LIQUIDITY");
uint256 balance0;
uint256 balance1;
{
// scope for _token{0,1}, avoids stack too deep errorsaddress _token0 = token0;
address _token1 = token1;
require(to != _token0 && to != _token1, "MagicswapV2: INVALID_TO");
if (amount0Out >0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokensif (amount1Out >0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
}
uint256 amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
uint256 amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
require(amount0In >0|| amount1In >0, "MagicswapV2: INSUFFICIENT_INPUT_AMOUNT");
{
// scope for reserve{0,1}Adjusted, avoids stack too deep errorsuint256 totalFee = IUniswapV2Factory(factory).getTotalFee(address(this));
uint256 balance0Adjusted = balance0.mul(BASIS_POINTS).sub(amount0In.mul(totalFee));
uint256 balance1Adjusted = balance1.mul(BASIS_POINTS).sub(amount1In.mul(totalFee));
require(
balance0Adjusted.mul(balance1Adjusted) >=uint256(_reserve0).mul(_reserve1).mul(BASIS_POINTS **2),
"MagicswapV2: K"
);
(balance0, balance1) = _takeFees(balance0Adjusted, balance1Adjusted, amount0In, amount1In);
}
_update(balance0, balance1, _reserve0, _reserve1);
emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
}
/// @dev Read TWAP pricefunctionobserve(uint32[] calldata secondsAgos)
externalviewoverridereturns (uint256[] memory priceCumulatives)
{
return observations.observe(_blockTimestamp(), secondsAgos, lastPrice, observationIndex, observationCardinality);
}
/// @dev Increase number of data points for price historyfunctionincreaseObservationCardinalityNext(uint16 _observationCardinalityNext) externaloverridelock{
uint16 observationCardinalityNextOld = observationCardinalityNext; // for the eventuint16 observationCardinalityNextNew =
observations.grow(observationCardinalityNextOld, _observationCardinalityNext);
observationCardinalityNext = observationCardinalityNextNew;
if (observationCardinalityNextOld != observationCardinalityNextNew) {
emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew);
}
}
/// @dev force balances to match reservesfunctionskim(address to) externallock{
address _token0 = token0; // gas savingsaddress _token1 = token1; // gas savings
_safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
_safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
}
/// @dev force reserves to match balancesfunctionsync() externallock{
_update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
}
}