Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare for post-launch NFT artwork #128

Merged
merged 12 commits into from
Jul 26, 2022
Merged

Prepare for post-launch NFT artwork #128

merged 12 commits into from
Jul 26, 2022

Conversation

danielattilasimon
Copy link
Contributor

@danielattilasimon danielattilasimon commented Jul 21, 2022

Addressed in this PR

This PR addresses the following points of #118.

Not burning

The NFT is no longer burnt when terminating the bond (either through chicken-in or chicken-out). As a consequence, bond supply only ever increases. Bond data is no longer deleted either. This allows bond data to be used in the generation of metadata/artwork even after the bond is terminated.

Since the bond is no longer burnt, we need an alternative way of preventing multiple termination (e.g. chickening out twice). A new field status is added to bond data, which is checked and updated by chicken-in/out. The same field can also be used by the artwork to determine which kind of visuals to render.

Transfer lockout period

To prevent potential scams where the sale of an active bond (which holds additional economical value on top of the artistic aspect of the NFT) is frontrun by a chicken-in/out by the bond's owner, we disallow transfers of the bond NFT for a configurable period of time after chicken-in/out. To facilitate this, a new field endTime is added to bond data, holding the timestamp of the block which terminated the bond. The NFT artwork may also use this field as additional source of (weak) randomness in generating the chickened in/out visuals.

Upgradeable NFT metadata

The computation of tokenURI() is delegated by BondNFT to an external contract. When constructing BondNFT, an initial delegate may be provided as a constructor parameter. If left zero, BondNFT will initially return empty string as tokenURI(), until the final delegate is set. This lets us decide whether we want to provide some preliminary metadata even before artwork launch, or not.

A final delegate can be set (once) through setArtworkAddress(). Upon calling this, the owner of BondNFT loses ownership over the contract.

Left open

The following points are not addressed by this PR, as there are still open questions.

  • Wrapper to terminate and transfer bond in one TX: we haven't decided whether this is worth it, or even if it's a good idea.
  • Transfer fee: we haven't fleshed out the specifications for this. Would we only activate this once artwork is upgraded? Is there a de facto standard for implementing transfer fees? (I've found https://eips.ethereum.org/EIPS/eip-2981, but OpenSea doesn't seem to support it).
  • Better source of randomness: currently, the only (weak) sources of randomness are the startTime and endTime of the bond, both of which come from block timestamps. If we want higher quality randomness, we should probably implement it ahead of artwork launch to avoid having a procedure for claiming the artwork.
  • Placeholder metadata: we may want to launch with some preliminary implementation of metadata (tokenURI()) so that bond NFTs show up with some description (or even some basic attributes like bonded amount, creation date, termination date) in wallets and marketplaces. Since this is implemented as a read-only function in an external contract, it may not need to be thoroughly audited.

@danielattilasimon danielattilasimon marked this pull request as ready for review July 22, 2022 03:47
if (bond.status != BondStatus.active) {
return 0;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add the final accrued bLUSD at the time termination to bond data? Will the artwork/metadata need that information?

@bingen
Copy link
Contributor

bingen commented Jul 25, 2022

For the first pending issue, “Wrapper to terminate and transfer bond in one TX”, we decided not to do it, to prioritize security. The potential use cases of the wrapper don’t seem so important.

@bingen
Copy link
Contributor

bingen commented Jul 25, 2022

A note about transfer fee: We have to make sure that pending bonds can be transferred without a fee, otherwise the sale of NFTs for the economical value doesn’t make sense (the fee value would be higher than the value of the time / opportunity cost of the pending bond).
The transfer fee would make more sense for chicken in state, which is expected to have more artistic value.

Copy link
Contributor

@bingen bingen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job! Looks good to me!

@@ -1,4 +1,4 @@
[default]
[profile.default]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still a newbie with foundry. What’s that for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was automatically changed by the latest version of forge after a foundryup. Something to do with deprecation.

}

// Tokens are never burnt, therefore total minted equals the total supply.
function totalMinted() external view returns (uint256) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn’t belong to any standard, right? Then I guess we can remove it.


_burn(_tokenID);
}
function tokenURI(uint256 _tokenID) public view virtual override returns (string memory) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can’t this be external?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I tried initially, but it won't compile. I guess because OpenZeppelin already implements it as a public view virtual, the overriding implementation has to have the same visibility.

Copy link
Contributor

@RickGriff RickGriff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great to me! Really nice and clean.

A minor suggestion: we could add individual bond property getters to the NFT contract itself, e.g. getBondAmount, getBondStartTime, etc. Could potentially be useful for marketplaces like OpenSea. IIRC Luchadore had that in an example NFT contract.

renounceOwnership();
}

function mint(address _bonder) external returns (uint256) {
requireCallerIsChickenBondsManager();
tokenSupply++;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was confused a little here as to where we actually increase totalSupply. now. I see now it happens in _beforeTokenTransfer when we _mint.

// the permanent increase should be N * NFT_RANDOMNESS_DIVISOR.
// It also means that as long as Permanent doesn’t change in that order of magnitude, attacker can try to manipulate
// only changing the event date.
uint256 constant public NFT_RANDOMNESS_DIVISOR = 1000e18;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice cost imposed on manipulators!

@bingen bingen merged commit a3cf768 into main Jul 26, 2022
@danielattilasimon danielattilasimon deleted the nft-artwork branch September 26, 2022 01:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants