There are a few different ways to create a dynamic NFT. For a standard ERC-721, you'll generally have a mint function that looks something like this:
function mint(address to, string memory _tokenURI) public {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, _tokenURI);
}
The token URI here represents all of the NFT's metadata. It is set using _setTokenURI(). (Note: depending on which version of Solidity and ERC-721 you use, _setTokenURI() may have been replaced.) The same general concept applies for rewriting this metadata. At its most generalized, you might have a function that looks like this:
function changeNft(uint256 _tokenId, string memory _newURI) public {
_setTokenURI(_tokenId, _newURI);
}
This would allow anyone to modify the NFT, with the metadata of their choice. From here, the big question is - under what conditions does the NFT change? You could have it change based on oracle data, or change every time the owner pays a certain amount. I recommend starting simply and building out.
One last note: I've used a few websites and APIs to test the evolution of my dynamic NFTs. None of them update quickly, which will make you think your code doesn't work. The fastest/simplest solution I've found is clicking the Refresh Metadata button on OpenSea.