How to create a resell token functionality in your NFT marketplace smart contract
Tags: nft, blockchain, solidity, web3
Published: 2021-12-07 21:59:58

This article is intended to be a continuation of @dabit3's great tutorial on how to create a [NFT Marketplace on Ethereum with Polygon](https://dev.to/dabit3/building-scalable-full-stack-apps-on-ethereum-with-polygon-2cfb). So, if you haven't started from there, I suggest you do. 

I'll be covering how to create a resell item feature to your marketplace, since this was an issue to me and to other developers to whom I have talked to. 

First, in the NFT contract, we'll add a function to transfer the token. You may be asking why I don't just simply use [ERC721's tranferFrom function](https://docs.openzeppelin.com/contracts/2.x/api/token/erc721#IERC721-transferFrom-address-address-uint256-). And the reason is that when I tested my functions, in some situations requests reverted with "transfer caller is not owner nor approved", even though msg.sender == ownerOf(tokenId) asserted true. So my solution was to write a custom transfer function. But feel free to share the solution for this strange bug in the comments if you know it. So this will be added to your NFT contract: 
```solidity
contract NFT is ERC721URIStorage {

    (...)

    function transferToken(address from, address to, uint256 tokenId) external {
        require(ownerOf(tokenId) == from, "From address must be token owner");
        _transfer(from, to, tokenId);
    }
  
}
```
The "external" visibility allows only other smart contracts to call the function. And the "require" statement checks if the caller is the token's current owner. 

Now, in the MarketPlace contract, you have to import the NFT contract to call the function, and it will look like this:
```solidity
import "./NFT.sol";
```

The second change I made was to add a "creator" attribute to the MarketItem struct, so it won't lose the information about who first minted the token between transfers: 
```solidity
struct MarketItem {
        uint256 itemId;
        address nftContract;
        uint256 tokenId;
        address payable creator;
        address payable seller;
        address payable owner;
        uint256 price;
        bool sold;
    }
```
That means you'll have to alter your other existing functions and add this attribute too. I also created a new event, to broadcast that the token is being sold again: 

```solidity
event ProductListed(
        uint256 indexed itemId
    );
```

Also, to write smaller functions and be able to reuse code, I created a [modifier function](https://docs.soliditylang.org/en/v0.8.10/contracts.html#function-modifiers) to prevent that others than the contract owner do the operation: 

```solidity
    modifier onlyItemOwner(uint256 id) {
        require(
            idToMarketItem[id].owner == msg.sender,
            "Only product owner can do this operation"
        );
        _;
    }
```
And finally, the function to (re)list the token in the marketplace: 
```solidity
function putItemToResell(address nftContract, uint256 itemId, uint256 newPrice)
        public
        payable
        nonReentrant
        onlyItemOwner(itemId)
    {
        uint256 tokenId = idToMarketItem[itemId].tokenId;
        require(newPrice > 0, "Price must be at least 1 wei");
        require(
            msg.value == listingPrice,
            "Price must be equal to listing price"
        );
        //instantiate a NFT contract object with the matching type
        NFT tokenContract = NFT(nftContract);
        //call the custom transfer token method   
        tokenContract.transferToken(msg.sender, address(this), tokenId);
        
        address oldOwner = idToMarketItem[itemId].owner;
        idToMarketItem[itemId].owner = payable(address(0));
        idToMarketItem[itemId].seller = oldOwner;
        idToMarketItem[itemId].price = newPrice;
        idToMarketItem[itemId].sold = false;
        _itemsSold.decrement();

        emit ProductListed(itemId);
    }
```
For testing: 
```javascript
const { expect } = require('chai')
const { ethers } = require("hardhat");

let market;
let nft;
let nftAddress;
let marketAddress;

beforeEach(async ()=>{
    const Market = await ethers.getContractFactory("NFTMarket");
    market = await Market.deploy();
    await market.deployed();

    marketAddress = market.address;

    const NFT = await ethers.getContractFactory("NFT");
    nft = await NFT.deploy(marketAddress);
    await nft.deployed();
    nftAddress = nft.address;
})

describe("Marketplace", () => {
   (...)
    it("should allow buyer to resell an owned item", async () => {
    const [, creator, buyer] = await ethers.getSigners();

    await nft.connect(creator).createToken("www.mytoken.com")

    const listingPrice = await market.getListingPrice();

    await market.connect(creator).createMarketItem(nftAddress, 1, 100, {value: listingPrice})

    await market.connect(buyer).createMarketSale(nftAddress, 1, {value: 100})
    
    await market.connect(buyer).putItemToResell(nftAddress, 1, 150, {value: listingPrice})

    const item = await market.fetchSingleItem(1)

    expect(item.seller).to.equal(buyer.address)
    expect(item.creator).to.equal(creator.address)
  })
})
```
And in the front-end:

```javascript
export async function resellOwnedItem(id, price, signer) {
  const marketContract = new ethers.Contract(
    nftmarketaddress,
    Market.abi,
    signer
  );

  const listingPrice = await marketContract.getListingPrice();
  const tx = await marketContract.putItemToResell(
    nftaddress,
    id,
    ethers.utils.parseUnits(price, "ether"),
    { value: listingPrice.toString() }
  );
  await tx.wait();
}
```
I'm taking the signer as an argument here but you can also fetch it in your function as @dabit3 did: 
```javascript
const web3Modal = new Web3Modal();
const connection = await web3Modal.connect();
const provider = new ethers.providers.Web3Provider(connection);
const signer = provider.getSigner();
```
And this is how I handled this task. If you found a better solution, please share in the comments. Thank you!