Skip to content

Instantly share code, notes, and snippets.

@genecyber
Created December 15, 2020 17:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save genecyber/5788f68ac37f425e165182c267d60ae7 to your computer and use it in GitHub Desktop.
Save genecyber/5788f68ac37f425e165182c267d60ae7 to your computer and use it in GitHub Desktop.
Upgradable NFT Trading contract Set
pragma solidity ^0.4.8;
import "./Resolver.sol";
contract EtherRouter {
Resolver resolver;
function EtherRouter(Resolver _resolver) {
resolver = _resolver;
}
function() payable {
uint r;
// Get routing information for the called function
var (destination, outsize) = resolver.lookup(msg.sig, msg.data);
// Make the call
assembly {
calldatacopy(mload(0x40), 0, calldatasize)
r := delegatecall(sub(gas, 700), destination, mload(0x40), calldatasize, mload(0x40), outsize)
}
// Throw if the call failed
if (r != 1) { throw;}
// Pass on the return value
assembly {
return(mload(0x40), outsize)
}
}
}
// SPDX-License-Identifier: MIT
pragma experimental ABIEncoderV2;
pragma solidity ^0.6.12;
import "./SafeMath.sol";
interface IERC20Token {
function transfer(address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
interface IERC721 {
function burn(uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function mint( address _to, uint256 _tokenId, string calldata _uri, string calldata _payload) external;
function ownerOf(uint256 _tokenId) external returns (address _owner);
function getApproved(uint256 _tokenId) external returns (address);
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;
}
interface BasicERC20 {
function burn(uint256 value) external;
function mint(address account, uint256 amount) external;
function decimals() external view returns (uint8);
}
contract NFTrade {
address resolver;
address payable private owner;
bool public initialized;
address public nftAddress;
address public paymentAddress;
address public recipientAddress;
uint256 public offerPrice = 0;
bool public payToAcceptOffer = false;
bool public payToMakeOffer = false;
bool public locked = false;
struct Offer {
uint tokenId;
address _from;
}
// event for EVM logging
event OwnerSet(address indexed oldOwner, address indexed newOwner);
mapping(uint => Offer[]) offers;
mapping(uint => Offer[]) rejected;
mapping(address => mapping(uint => Offer[])) offered;
mapping(uint => Offer[]) accepted;
modifier isOwner() {
// If the first argument of 'require' evaluates to 'false', execution terminates and all
// changes to the state and to Ether balances are reverted.
// This used to consume all gas in old EVM versions, but not anymore.
// It is often a good idea to use 'require' to check if functions are called correctly.
// As a second argument, you can also provide an explanation about what went wrong.
require(msg.sender == owner, "Caller is not owner");
_;
}
modifier notLocked() {
require(!locked, "Contract is locked");
_;
}
constructor() public {
}
function init(address _nftAddress, address _paymentAddress, address _recipientAddress) public {
require(!initialized, 'Already initialized');
initialized = true;
owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor
emit OwnerSet(address(0), owner);
nftAddress = _nftAddress;
paymentAddress = _paymentAddress;
recipientAddress = _recipientAddress;
}
function getVersion() public pure returns (uint) {
return 1;
}
/**
* @dev Change owner
* @param newOwner address of new owner
*/
function transferOwnership(address payable newOwner) public isOwner {
emit OwnerSet(owner, newOwner);
owner = newOwner;
}
/**
* @dev Return owner address
* @return address of owner
*/
function getOwner() external view returns (address) {
return owner;
}
function acceptOffer(uint _tokenId, uint index) public notLocked {
Offer memory _offer = offers[_tokenId][index];
IERC721 nftToken = IERC721(nftAddress);
IERC20Token paymentToken = IERC20Token(paymentAddress);
require(nftToken.ownerOf(_tokenId) == msg.sender,'Sender is not owner of NFT');
require(nftToken.ownerOf(_offer.tokenId) == _offer._from, 'NFT not owned by offerer');
require(nftToken.getApproved(_offer.tokenId) == address(this), 'Handler unable to transfer offer NFT');
require(nftToken.getApproved(_tokenId) == address(this), 'Handler unable to transfer NFT');
if (offerPrice > 0 && payToAcceptOffer) {
require(paymentToken.allowance(msg.sender, address(this)) >= offerPrice, 'Handler unable take payment for offer');
require(paymentToken.balanceOf(msg.sender) >= offerPrice, 'Insufficient Balance for payment');
require(paymentToken.transferFrom(msg.sender, address(recipientAddress), offerPrice), 'Payment error');
}
nftToken.safeTransferFrom(_offer._from, msg.sender, _offer.tokenId);
nftToken.safeTransferFrom(msg.sender, _offer._from, _tokenId);
delete offers[_tokenId];
delete offered[_offer._from][_offer.tokenId];
accepted[_tokenId].push(_offer);
}
function addOffer(uint256 _tokenId, uint256 _for) public notLocked {
IERC721 nftToken = IERC721(nftAddress);
IERC20Token paymentToken = IERC20Token(paymentAddress);
require(nftToken.ownerOf(_tokenId) == msg.sender, 'Sender not owner of NFT');
require(nftToken.getApproved(_tokenId) == address(this), 'Handler unable to transfer NFT');
if (offerPrice > 0 && payToMakeOffer) {
require(paymentToken.allowance(msg.sender, address(this)) >= offerPrice, 'Handler unable take payment for offer');
require(paymentToken.balanceOf(msg.sender) >= offerPrice, 'Insufficient Balance for payment');
require(paymentToken.transferFrom(msg.sender, address(recipientAddress), offerPrice), 'Payment error');
}
offers[_for].push(Offer(_tokenId, msg.sender));
offered[msg.sender][_tokenId].push(Offer(_for, msg.sender));
}
function rejectOffer(uint256 _tokenId, uint index) public notLocked {
Offer memory _offer = offers[_tokenId][index];
IERC721 nftToken = IERC721(nftAddress);
require(nftToken.ownerOf(_tokenId) == msg.sender,'Sender is not owner of NFT');
rejected[_tokenId].push(_offer);
delete offers[_tokenId][index];
delete offered[_offer._from][_offer.tokenId];
}
function withdrawOffer(uint256 _tokenId, uint index) public notLocked {
Offer memory _offer = offers[_tokenId][index];
IERC721 nftToken = IERC721(nftAddress);
require(nftToken.ownerOf(_offer.tokenId) == msg.sender,'Sender is not owner of offer NFT');
delete offers[_tokenId][index];
delete offered[_offer._from][_offer.tokenId];
}
function togglePayToMakeOffer() public isOwner {
payToMakeOffer = !payToMakeOffer;
}
function togglePayToAcceptOffer() public isOwner {
payToAcceptOffer = !payToAcceptOffer;
}
function toggleLocked() public isOwner {
locked = !locked;
}
function getOffer(uint256 _tokenId, uint index) public view returns (Offer memory) {
return offers[_tokenId][index];
}
function getOffered(uint256 _tokenId) public view returns (Offer[] memory) {
return offered[msg.sender][_tokenId];
}
function getOfferCount(uint256 _tokenId) public view returns (uint) {
return offers[_tokenId].length;
}
function getAcceptedOffers(uint256 _tokenId) public view returns (Offer[] memory) {
return accepted[_tokenId];
}
function getRejectedOffers(uint256 _tokenId) public view returns (Offer[] memory) {
return rejected[_tokenId];
}
function changeOfferPrice(uint256 _price) public isOwner {
offerPrice = _price;
}
function changeRecipientAddress(address _recipientAddress) public isOwner {
recipientAddress = _recipientAddress;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment