Polygon Basic Integration

OpenSea recently began supporting Polygon NFTs on its platform. Follow this quick tutorial to make your Polygon (formerly Matic) contract enabled on OpenSea with gasless minting!

Polygon (formerly Matic) Blockchain

If you're developing on the Polygon L2 blockchain, please ensure the following:

  1. In your isApprovedForAll method, you should allow OpenSea's 0x proxy contract to operate on your NFT's contract. OpenSea has a different proxy contract address for ERC721 and ERC115, so make sure you are using the correct one.

📘

Why am I doing this?

In order to allow OpenSea to transact on your assets (such as selling and transferring assets), OpenSea's smart contract must be allowed to operate on your smart contract. Typically, you (a seller) would have to call your smart contract's setApprovalForAll() method with OpenSea's address to approve it as an operator, which would cost you gas.

Overriding the isApprovedForAll() to automatically bypass the ERC721 / ERC1155's default implementation if the operator is OpenSea's proxy contract allows you to begin selling items with OpenSea without paying expensive gas fees.

This will reduce friction for you as a seller when you list items on OpenSea, and we think that's pretty awesome.

Code Example for ERC721

/**
   * Override isApprovedForAll to auto-approve OS's proxy contract
   */
    function isApprovedForAll(
        address _owner,
        address _operator
    ) public override view returns (bool isOperator) {
      // if OpenSea's ERC721 Proxy Address is detected, auto-return true
        if (_operator == address(0x58807baD0B376efc12F5AD86aAc70E78ed67deaE)) {
            return true;
        }
        
        // otherwise, use the default ERC721.isApprovedForAll()
        return ERC721.isApprovedForAll(_owner, _operator);
    }

Code Example for ERC1155

/**
   * Override isApprovedForAll to auto-approve OS's proxy contract
   */
    function isApprovedForAll(
        address _owner,
        address _operator
    ) public override view returns (bool isOperator) {
        // if OpenSea's ERC1155 Proxy Address is detected, auto-return true
       if (_operator == address(0x207Fa8Df3a17D96Ca7EA4f2893fcdCb78a304101)) {
            return true;
        }
        // otherwise, use the default ERC1155.isApprovedForAll()
        return ERC1155.isApprovedForAll(_owner, _operator);
    }

This will mean that the user doesn't have to pay gas the first time they list an item on OpenSea. This is particularly important on Polygon, where gas is paid in MATIC, which is still somewhat difficult for users to obtain.

  1. Your smart contracts support meta transactions so that OpenSea can abstract gas payments for users for methods such as transfers. In order to do this, you'll want to do the following:
  2. Inherit the latest ERC721 / ERC1155 standard from OpenZeppelin.
  3. Override MetaTransaction's _msgSender() to use a ContentMixin custom implementation. Code example below (derived from our GitHub meta-transaction repo, linked below):
/**
 * https://github.com/maticnetwork/pos-portal/blob/master/contracts/common/ContextMixin.sol
 */
abstract contract ContextMixin {
    function msgSender()
        internal
        view
        returns (address payable sender)
    {
        if (msg.sender == address(this)) {
            bytes memory array = msg.data;
            uint256 index = msg.data.length;
            assembly {
                // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.
                sender := and(
                    mload(add(array, index)),
                    0xffffffffffffffffffffffffffffffffffffffff
                )
            }
        } else {
            sender = payable(msg.sender);
        }
        return sender;
    }
}

....

contract MyPolygonERC721NFT is ERC721, ContextMixin, ERC2771Context {
...
  
    /**
     * This is used instead of msg.sender as transactions won't be sent by the original token owner, but by OpenSea.
     */
    function _msgSender()
        internal
        override
        view
        returns (address sender)
    {
        return ContextMixin.msgSender();
    }
...
}

🚧

OpenZeppelin's contracts

For the the isApprovedForAll() examples above to work as expected, make sure you are using a version of OpenZeppelin's ERC721 / ERC1155 contracts version at least 4.x. Older version's of OpenZeppelin's contracts will not support this mechanism for allowing OpenSea's proxy contracts to transact on their behalf.

Ideally, you are always using the latest releases for all your dependencies when it comes to development!

Sample implementations of the above can be found here:
https://github.com/ProjectOpenSea/meta-transactions/tree/main/contracts

Read more about gassless transactions and metatransactions here:
https://docs.openzeppelin.com/learn/sending-gasless-transactions