You need to block Opensea's contracts from transferring your NFT. You can do it by modifying its solidity source code, so if it's already deployed it's too late.
The most straightforward method is blocking directly users from approving Opensea's contracts. Opensea can use the Seaport contract for transfers, but also conduits, so we need to block both.
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
interface IConduitController {
function getKey(address conduit) external view returns(bytes32);
}
contract MyNFT is ERC721 {
address private constant SEAPORT = 0x00000000006c3852cbEf3e08E8dF289169EdE581;
address private constant CONDUIT_CONTROLLER = 0x00000000F9490004C11Cef243f5400493c00Ad63;
constructor() ERC721("TEST", "TEST") {}
function _requireNotOpenSea(address to) internal view {
// Check spender isn't Seaport.
require(to != SEAPORT, "OPENSEA NOT ALLOWED");
// Check spender isn't a conduit.
// First we call the controller for the corresponding key:
// - if(success) -> the address is a valid conduit
// - else -> the address isn't a conduit
(bool success, ) = CONDUIT_CONTROLLER.staticcall(abi.encodeWithSelector(IConduitController.getKey.selector, to));
require(!success, "OPENSEA NOT ALLOWED");
}
function approve(address to, uint256 tokenId) public virtual override {
_requireNotOpenSea(to);
super.approve(to, tokenId);
}
function setApprovalForAll(address operator, bool approved) public virtual override {
_requireNotOpenSea(operator);
super.setApprovalForAll(operator, approved);
}
}