Cargando el resto de esta comparación.

Untitled Diff

Creado El diff nunca expira
498 eliminaciones
557 líneas
621 adiciones
704 líneas
// SPDX-License-Identifier: MIT
/**
pragma solidity ^0.8.0;
*Submitted for verification at Etherscan.io on 2022-02-01

*/
// OpenZeppelin contracts
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

// LooksRare interfaces
import {ICurrencyManager} from "./interfaces/ICurrencyManager.sol";
import {IExecutionManager} from "./interfaces/IExecutionManager.sol";
import {IExecutionStrategy} from "./interfaces/IExecutionStrategy.sol";
import {IRoyaltyFeeManager} from "./interfaces/IRoyaltyFeeManager.sol";
import {ILooksRareExchange} from "./interfaces/ILooksRareExchange.sol";
import {ITransferManagerNFT} from "./interfaces/ITransferManagerNFT.sol";
import {ITransferSelectorNFT} from "./interfaces/ITransferSelectorNFT.sol";
import {IWETH} from "./interfaces/IWETH.sol";


// LooksRare libraries
pragma solidity 0.4.26;
import {OrderTypes} from "./libraries/OrderTypes.sol";
import {SignatureChecker} from "./libraries/SignatureChecker.sol";
contract LooksRareExchange is ILooksRareExchange, ReentrancyGuard, Ownable {
using SafeERC20 for IERC20;


using OrderTypes for OrderTypes.MakerOrder;
library SafeMath {
using OrderTypes for OrderTypes.TakerOrder;


address public immutable WETH;
/**
bytes32 public immutable DOMAIN_SEPARATOR;
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
if (a == 0) {
return 0;
}
c = a * b;
assert(c / a == b);
return c;
}


address public protocolFeeRecipient;
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return a / b;
}


ICurrencyManager public currencyManager;
/**
IExecutionManager public executionManager;
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
IRoyaltyFeeManager public royaltyFeeManager;
*/
ITransferSelectorNFT public transferSelectorNFT;
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}


mapping(address => uint256) public userMinOrderNonce;
/**
mapping(address => mapping(uint256 => bool)) private _isUserOrderNonceExecutedOrCancelled;
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a + b;
assert(c >= a);
return c;
}
}


event CancelAllOrders(address indexed user, uint256 newMinNonce);
contract Ownable {
event CancelMultipleOrders(address indexed user, uint256[] orderNonces);
address public owner;
event NewCurrencyManager(address indexed currencyManager);
event NewExecutionManager(address indexed executionManager);
event NewProtocolFeeRecipient(address indexed protocolFeeRecipient);
event NewRoyaltyFeeManager(address indexed royaltyFeeManager);
event NewTransferSelectorNFT(address indexed transferSelectorNFT);


event RoyaltyPayment(
address indexed collection,
uint256 indexed tokenId,
address indexed royaltyRecipient,
address currency,
uint256 amount
);


event TakerAsk(
event OwnershipRenounced(address indexed previousOwner);
bytes32 orderHash, // bid hash of the maker order
event OwnershipTransferred(
uint256 orderNonce, // user order nonce
address indexed previousOwner,
address indexed taker, // sender address for the taker ask order
address indexed newOwner
address indexed maker, // maker address of the initial bid order
address indexed strategy, // strategy that defines the execution
address currency, // currency address
address collection, // collection address
uint256 tokenId, // tokenId transferred
uint256 amount, // amount of tokens transferred
uint256 price // final transacted price
);
);


event TakerBid(
bytes32 orderHash, // ask hash of the maker order
uint256 orderNonce, // user order nonce
address indexed taker, // sender address for the taker bid order
address indexed maker, // maker address of the initial ask order
address indexed strategy, // strategy that defines the execution
address currency, // currency address
address collection, // collection address
uint256 tokenId, // tokenId transferred
uint256 amount, // amount of tokens transferred
uint256 price // final transacted price
);


/**
/**
* @notice Constructor
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* @param _currencyManager currency manager address
* account.
* @param _executionManager execution manager address
* @param _royaltyFeeManager royalty fee manager address
* @param _WETH wrapped ether address (for other chains, use wrapped native asset)
* @param _protocolFeeRecipient protocol fee recipient
*/
*/
constructor(
constructor() public {
address _currencyManager,
owner = msg.sender;
address _executionManager,
address _royaltyFeeManager,
address _WETH,
address _protocolFeeRecipient
) {
// Calculate the domain separator
DOMAIN_SEPARATOR = keccak256(
abi.encode(
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
0xda9101ba92939daf4bb2e18cd5f942363b9297fbc3232c9dd964abb1fb70ed71, // keccak256("LooksRareExchange")
0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6, // keccak256(bytes("1")) for versionId = 1
block.chainid,
Text moved to lines 513-515
address(this)
)
);

currencyManager = ICurrencyManager(_currencyManager);
executionManager = IExecutionManager(_executionManager);
royaltyFeeManager = IRoyaltyFeeManager(_royaltyFeeManager);
WETH = _WETH;
protocolFeeRecipient = _protocolFeeRecipient;
}
}


/**
/**
* @notice Cancel all pending orders for a sender
* @dev Throws if called by any account other than the owner.
* @param minNonce minimum user nonce
*/
*/
function cancelAllOrdersForSender(uint256 minNonce) external {
modifier onlyOwner() {
require(minNonce > userMinOrderNonce[msg.sender], "Cancel: Order nonce lower than current");
require(msg.sender == owner);
require(minNonce < userMinOrderNonce[msg.sender] + 500000, "Cancel: Cannot cancel more orders");
_;
userMinOrderNonce[msg.sender] = minNonce;

emit CancelAllOrders(msg.sender, minNonce);
}
}


/**
/**
* @notice Cancel maker orders
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param orderNonces array of order nonces
* @param newOwner The address to transfer ownership to.
*/
*/
function cancelMultipleMakerOrders(uint256[] calldata orderNonces) external {
function transferOwnership(address newOwner) public onlyOwner {
require(orderNonces.length > 0, "Cancel: Cannot be empty");
require(newOwner != address(0));

emit OwnershipTransferred(owner, newOwner);
for (uint256 i = 0; i < orderNonces.length; i++) {
owner = newOwner;
require(orderNonces[i] >= userMinOrderNonce[msg.sender], "Cancel: Order nonce lower than current");
_isUserOrderNonceExecutedOrCancelled[msg.sender][orderNonces[i]] = true;
}

emit CancelMultipleOrders(msg.sender, orderNonces);
}
}


/**
/**
* @notice Match ask with a taker bid order using ETH
* @dev Allows the current owner to relinquish control of the contract.
* @param takerBid taker bid order
* @param makerAsk maker ask order
*/
*/
function matchAskWithTakerBidUsingETHAndWETH(
function renounceOwnership() public onlyOwner {
OrderTypes.TakerOrder calldata takerBid,
emit OwnershipRenounced(owner);
OrderTypes.MakerOrder calldata makerAsk
owner = address(0);
) external payable override nonReentrant {
}
require((makerAsk.isOrderAsk) && (!takerBid.isOrderAsk), "Order: Wrong sides");
require(makerAsk.currency == WETH, "Order: Currency must be WETH");
require(msg.sender == takerBid.taker, "Order: Taker must be the sender");

// If not enough ETH to cover the price, use WETH
if (takerBid.price > msg.value) {
IERC20(WETH).safeTransferFrom(msg.sender, address(this), (takerBid.price - msg.value));
} else {
require(takerBid.price == msg.value, "Order: Msg.value too high");
}
}


// Wrap ETH sent to this contract
contract ERC20Basic {
IWETH(WETH).deposit{value: msg.value}();
function totalSupply() public view returns (uint256);

function balanceOf(address who) public view returns (uint256);
// Check the maker ask order
function transfer(address to, uint256 value) public returns (bool);
bytes32 askHash = makerAsk.hash();
event Transfer(address indexed from, address indexed to, uint256 value);
_validateOrder(makerAsk, askHash);
}

// Retrieve execution parameters
(bool isExecutionValid, uint256 tokenId, uint256 amount) = IExecutionStrategy(makerAsk.strategy)
.canExecuteTakerBid(takerBid, makerAsk);

require(isExecutionValid, "Strategy: Execution invalid");

// Update maker ask order status to true (prevents replay)
_isUserOrderNonceExecutedOrCancelled[makerAsk.signer][makerAsk.nonce] = true;


// Execution part 1/2
contract ERC20 is ERC20Basic {
_transferFeesAndFundsWithWETH(
function allowance(address owner, address spender)
makerAsk.strategy,
public view returns (uint256);
makerAsk.collection,
tokenId,
makerAsk.signer,
takerBid.price,
makerAsk.minPercentageToAsk
);


// Execution part 2/2
function transferFrom(address from, address to, uint256 value)
_transferNonFungibleToken(makerAsk.collection, makerAsk.signer, takerBid.taker, tokenId, amount);
public returns (bool);


emit TakerBid(
function approve(address spender, uint256 value) public returns (bool);
askHash,
event Approval(
makerAsk.nonce,
address indexed owner,
takerBid.taker,
address indexed spender,
makerAsk.signer,
uint256 value
makerAsk.strategy,
makerAsk.currency,
makerAsk.collection,
tokenId,
amount,
takerBid.price
);
);
}
}

library ArrayUtils {


/**
/**
* @notice Match a takerBid with a matchAsk
* Replace bytes in an array with bytes in another array, guarded by a bitmask
* @param takerBid taker bid order
* Efficiency of this function is a bit unpredictable because of the EVM's word-specific model (arrays under 32 bytes will be slower)
* @param makerAsk maker ask order
*
* @dev Mask must be the size of the byte array. A nonzero byte means the byte array can be changed.
* @param array The original array
* @param desired The target array
* @param mask The mask specifying which bits can be changed
* @return The updated byte array (the parameter will be modified inplace)
*/
*/
function matchAskWithTakerBid(OrderTypes.TakerOrder calldata takerBid, OrderTypes.MakerOrder calldata makerAsk)
function guardedArrayReplace(bytes memory array, bytes memory desired, bytes memory mask)
external
internal
override
pure
nonReentrant
{
{
require((makerAsk.isOrderAsk) && (!takerBid.isOrderAsk), "Order: Wrong sides");
require(array.length == desired.length);
require(msg.sender == takerBid.taker, "Order: Taker must be the sender");
require(array.length == mask.length);

// Check the maker ask order
bytes32 askHash = makerAsk.hash();
_validateOrder(makerAsk, askHash);

(bool isExecutionValid, uint256 tokenId, uint256 amount) = IExecutionStrategy(makerAsk.strategy)
.canExecuteTakerBid(takerBid, makerAsk);

require(isExecutionValid, "Strategy: Execution invalid");

// Update maker ask order status to true (prevents replay)
_isUserOrderNonceExecutedOrCancelled[makerAsk.signer][makerAsk.nonce] = true;


// Execution part 1/2
uint words = array.length / 0x20;
_transferFeesAndFunds(
uint index = words * 0x20;
makerAsk.strategy,
assert(index / 0x20 == words);
makerAsk.collection,
uint i;
tokenId,
makerAsk.currency,
msg.sender,
makerAsk.signer,
takerBid.price,
makerAsk.minPercentageToAsk
);


// Execution part 2/2
for (i = 0; i < words; i++) {
_transferNonFungibleToken(makerAsk.collection, makerAsk.signer, takerBid.taker, tokenId, amount);
/* Conceptually: array[i] = (!mask[i] && array[i]) || (mask[i] && desired[i]), bitwise in word chunks. */
assembly {
let commonIndex := mul(0x20, add(1, i))
let maskValue := mload(add(mask, commonIndex))
mstore(add(array, commonIndex), or(and(not(maskValue), mload(add(array, commonIndex))), and(maskValue, mload(add(desired, commonIndex)))))
}
}


emit TakerBid(
/* Deal with the last section of the byte array. */
askHash,
if (words > 0) {
makerAsk.nonce,
/* This overlaps with bytes already set but is still more efficient than iterating through each of the remaining bytes individually. */
takerBid.taker,
i = words;
makerAsk.signer,
assembly {
makerAsk.strategy,
let commonIndex := mul(0x20, add(1, i))
makerAsk.currency,
let maskValue := mload(add(mask, commonIndex))
makerAsk.collection,
mstore(add(array, commonIndex), or(and(not(maskValue), mload(add(array, commonIndex))), and(maskValue, mload(add(desired, commonIndex)))))
tokenId,
}
amount,
} else {
takerBid.price
/* If the byte array is shorter than a word, we must unfortunately do the whole thing bytewise.
);
(bounds checks could still probably be optimized away in assembly, but this is a rare case) */
for (i = index; i < array.length; i++) {
array[i] = ((mask[i] ^ 0xff) & array[i]) | (mask[i] & desired[i]);
}
}
}
}


/**
/**
* @notice Match a takerAsk with a makerBid
* Test if two arrays are equal
* @param takerAsk taker ask order
* @param a First array
* @param makerBid maker bid order
* @param b Second array
* @return Whether or not all bytes in the arrays are equal
*/
*/
function matchBidWithTakerAsk(OrderTypes.TakerOrder calldata takerAsk, OrderTypes.MakerOrder calldata makerBid)
function arrayEq(bytes memory a, bytes memory b)
external
internal
override
pure
nonReentrant
returns (bool)
{
{
require((!makerBid.isOrderAsk) && (takerAsk.isOrderAsk), "Order: Wrong sides");
return keccak256(a) == keccak256(b);
require(msg.sender == takerAsk.taker, "Order: Taker must be the sender");

// Check the maker bid order
bytes32 bidHash = makerBid.hash();
_validateOrder(makerBid, bidHash);

(bool isExecutionValid, uint256 tokenId, uint256 amount) = IExecutionStrategy(makerBid.strategy)
.canExecuteTakerAsk(takerAsk, makerBid);

require(isExecutionValid, "Strategy: Execution invalid");

// Update maker bid order status to true (prevents replay)
_isUserOrderNonceExecutedOrCancelled[makerBid.signer][makerBid.nonce] = true;

// Execution part 1/2
_transferNonFungibleToken(makerBid.collection, msg.sender, makerBid.signer, tokenId, amount);

// Execution part 2/2
_transferFeesAndFunds(
makerBid.strategy,
makerBid.collection,
tokenId,
makerBid.currency,
makerBid.signer,
takerAsk.taker,
takerAsk.price,
takerAsk.minPercentageToAsk
);

emit TakerAsk(
bidHash,
makerBid.nonce,
takerAsk.taker,
makerBid.signer,
makerBid.strategy,
makerBid.currency,
makerBid.collection,
tokenId,
amount,
takerAsk.price
);
}
}


/**
/**
* @notice Update currency manager
* Unsafe write byte array into a memory location
* @param _currencyManager new currency manager address
*
* @param index Memory location
* @param source Byte array to write
* @return End memory index
*/
*/
function updateCurrencyManager(address _currencyManager) external onlyOwner {
function unsafeWriteBytes(uint index, bytes source)
require(_currencyManager != address(0), "Owner: Cannot be null address");
internal
currencyManager = ICurrencyManager(_currencyManager);
pure
emit NewCurrencyManager(_currencyManager);
returns (uint)
{
if (source.length > 0) {
assembly {
let length := mload(source)
let end := add(source, add(0x20, length))
let arrIndex := add(source, 0x20)
let tempIndex := index
for { } eq(lt(arrIndex, end), 1) {
arrIndex := add(arrIndex, 0x20)
tempIndex := add(tempIndex, 0x20)
} {
mstore(tempIndex, mload(arrIndex))
}
index := add(index, length)
}
}
return index;
}
}


/**
/**
* @notice Update execution manager
* Unsafe write address into a memory location
* @param _executionManager new execution manager address
*
* @param index Memory location
* @param source Address to write
* @return End memory index
*/
*/
function updateExecutionManager(address _executionManager) external onlyOwner {
function unsafeWriteAddress(uint index, address source)
require(_executionManager != address(0), "Owner: Cannot be null address");
internal
executionManager = IExecutionManager(_executionManager);
pure
emit NewExecutionManager(_executionManager);
returns (uint)
{
uint conv = uint(source) << 0x60;
assembly {
mstore(index, conv)
index := add(index, 0x14)
}
return index;
}
}


/**
/**
* @notice Update protocol fee and recipient
* Unsafe write address into a memory location using entire word
* @param _protocolFeeRecipient new recipient for protocol fees
*
* @param index Memory location
* @param source uint to write
* @return End memory index
*/
*/
function updateProtocolFeeRecipient(address _protocolFeeRecipient) external onlyOwner {
function unsafeWriteAddressWord(uint index, address source)
protocolFeeRecipient = _protocolFeeRecipient;
internal
emit NewProtocolFeeRecipient(_protocolFeeRecipient);
pure
returns (uint)
{
assembly {
mstore(index, source)
index := add(index, 0x20)
}
return index;
}
}


/**
/**
* @notice Update royalty fee manager
* Unsafe write uint into a memory location
* @param _royaltyFeeManager new fee manager address
*
* @param index Memory location
* @param source uint to write
* @return End memory index
*/
*/
function updateRoyaltyFeeManager(address _royaltyFeeManager) external onlyOwner {
function unsafeWriteUint(uint index, uint source)
require(_royaltyFeeManager != address(0), "Owner: Cannot be null address");
internal
royaltyFeeManager = IRoyaltyFeeManager(_royaltyFeeManager);
pure
emit NewRoyaltyFeeManager(_royaltyFeeManager);
returns (uint)
{
assembly {
mstore(index, source)
index := add(index, 0x20)
}
return index;
}
}


/**
/**
* @notice Update transfer selector NFT
* Unsafe write uint8 into a memory location
* @param _transferSelectorNFT new transfer selector address
*
* @param index Memory location
* @param source uint8 to write
* @return End memory index
*/
*/
function updateTransferSelectorNFT(address _transferSelectorNFT) external onlyOwner {
function unsafeWriteUint8(uint index, uint8 source)
require(_transferSelectorNFT != address(0), "Owner: Cannot be null address");
internal
transferSelectorNFT = ITransferSelectorNFT(_transferSelectorNFT);
pure

returns (uint)
emit NewTransferSelectorNFT(_transferSelectorNFT);
{
assembly {
mstore8(index, source)
index := add(index, 0x1)
}
return index;
}
}


/**
/**
* @notice Check whether user order nonce is executed or cancelled
* Unsafe write uint8 into a memory location using entire word
* @param user address of user
*
* @param orderNonce nonce of the order
* @param index Memory location
* @param source uint to write
* @return End memory index
*/
*/
function isUserOrderNonceExecutedOrCancelled(address user, uint256 orderNonce) external view returns (bool) {
function unsafeWriteUint8Word(uint index, uint8 source)
return _isUserOrderNonceExecutedOrCancelled[user][orderNonce];
internal
pure
returns (uint)
{
assembly {
mstore(index, source)
index := add(index, 0x20)
}
return index;
}
}


/**
/**
* @notice Transfer fees and funds to royalty recipient, protocol, and seller
* Unsafe write bytes32 into a memory location using entire word
* @param strategy address of the execution strategy
*
* @param collection non fungible token address for the transfer
* @param index Memory location
* @param tokenId tokenId
* @param source uint to write
* @param currency currency being used for the purchase (e.g., WETH/USDC)
* @return End memory index
* @param from sender of the funds
* @param to seller's recipient
* @param amount amount being transferred (in currency)
* @param minPercentageToAsk minimum percentage of the gross amount that goes to ask
*/
*/