-
Notifications
You must be signed in to change notification settings - Fork 9
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
Force royalties on ERC721 #55
base: main
Are you sure you want to change the base?
Changes from 4 commits
2d5f101
d1ed23e
348a837
37f15f2
dffe344
2a6f88b
704470d
759835b
cbd6a99
faf00b3
a9f9856
38771fe
c3c44d9
e2ec909
a42bbc9
d8d07a1
3cb9c2b
30aedb9
5627f70
7720741
8f89230
cfc3aa6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -92,6 +92,14 @@ event ApprovalForAll: | |||||||||||||||||
operator: indexed(address) | ||||||||||||||||||
approved: bool | ||||||||||||||||||
|
||||||||||||||||||
{%- if cookiecutter.force_royalties == 'y' %} | ||||||||||||||||||
# @dev This emits when the owner of the contract withdrawals royalties | ||||||||||||||||||
# @param amount withdrawal by owner of smart contract | ||||||||||||||||||
event RoyaltiesWithdrawn: | ||||||||||||||||||
amount: indexed(uint256) | ||||||||||||||||||
|
||||||||||||||||||
{%- endif %} | ||||||||||||||||||
|
||||||||||||||||||
owner: public(address) | ||||||||||||||||||
isMinter: public(HashMap[address, bool]) | ||||||||||||||||||
|
||||||||||||||||||
|
@@ -108,6 +116,14 @@ isApprovedForAll: public(HashMap[address, HashMap[address, bool]]) | |||||||||||||||||
|
||||||||||||||||||
# @dev Mapping from NFT ID to approved address. | ||||||||||||||||||
idToApprovals: public(HashMap[uint256, address]) | ||||||||||||||||||
{%- if cookiecutter.force_royalties == 'y' %} | ||||||||||||||||||
# @dev the last balance of the smart contract that stores the royalties of the contract creator | ||||||||||||||||||
# this balance is reset to 0 the moment the creator withdraws royalties | ||||||||||||||||||
lastBalance: uint256 | ||||||||||||||||||
|
||||||||||||||||||
# @dev we check this value to make sure royalties have been paid | ||||||||||||||||||
royaltyAmount: uint256 | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would suggest There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, add a note here to explore means of setting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also also, need to set this in |
||||||||||||||||||
{%- endif %} | ||||||||||||||||||
|
||||||||||||||||||
{%- if cookiecutter.permitable == 'y' %} | ||||||||||||||||||
############ ERC-4494 ############ | ||||||||||||||||||
|
@@ -301,6 +317,49 @@ def royaltyInfo(_tokenId: uint256, _salePrice: uint256) -> (address, uint256): | |||||||||||||||||
return self.owner, royalty | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This actually needs to be update to integrate well
Suggested change
|
||||||||||||||||||
{%- endif %} | ||||||||||||||||||
|
||||||||||||||||||
{%- if cookiecutter.force_royalties == 'y' %} | ||||||||||||||||||
# Helper functions in case market place does not support royalties to execute by this contract with _deductRoyalties() | ||||||||||||||||||
@internal | ||||||||||||||||||
@view | ||||||||||||||||||
def _royaltyInfo(_tokenId: uint256, _salePrice: uint256) -> (address, uint256): | ||||||||||||||||||
""" | ||||||||||||||||||
/// @notice Called with the sale price to determine how much royalty | ||||||||||||||||||
// is owed and to whom. Important; Not all marketplaces respect this, e.g. OpenSea | ||||||||||||||||||
/// @param _tokenId - the NFT asset queried for royalty information | ||||||||||||||||||
/// @param _salePrice - the sale price of the NFT asset specified by _tokenId | ||||||||||||||||||
/// @return receiver - address of who should be sent the royalty payment | ||||||||||||||||||
/// @return owner address and royaltyAmount - the royalty payment amount for _salePrice | ||||||||||||||||||
""" | ||||||||||||||||||
|
||||||||||||||||||
royalty: uint256 = convert(convert(_salePrice, decimal) * ROYALTY_TO_APPLY_TO_PRICE, uint256) # Percentage that accepts decimals | ||||||||||||||||||
return self.owner, royalty | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and then here do:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @fubuloubu this |
||||||||||||||||||
|
||||||||||||||||||
@internal | ||||||||||||||||||
def royaltyChecker(tokenId: uint256): | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @fubuloubu why include the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a future scenario you might want to use it to detect which marketplace is triggering the call to update the minimum royalty amount For now you can probably skip the check if |
||||||||||||||||||
# check if royalties hace been paid | ||||||||||||||||||
if self.balance < self.lastBalance: | ||||||||||||||||||
self._deductRoyalties(tokenId) | ||||||||||||||||||
# equal the contract balance to the lastBalance for future checks | ||||||||||||||||||
self.lastBalance = self.balance | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @fubuloubu for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's the trick! It'll automatically be 0 because the balance will be physically transferred via There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
From a tax perspective, income is income the moment it's earned (made available to you under your full control), so I don't think there's any real tax optimization tricks here |
||||||||||||||||||
|
||||||||||||||||||
@external | ||||||||||||||||||
@payable | ||||||||||||||||||
def withdrawRoyalties(): | ||||||||||||||||||
assert msg.sender == self.owner | ||||||||||||||||||
amount: uint256 = self.balance | ||||||||||||||||||
send(self.owner, amount) | ||||||||||||||||||
self.lastBalance = 0 | ||||||||||||||||||
log RoyaltiesWithdrawn(amount) | ||||||||||||||||||
|
||||||||||||||||||
@internal | ||||||||||||||||||
@payable | ||||||||||||||||||
def _deductRoyalties(tokenId: uint256): | ||||||||||||||||||
# we calculate royalties and owners address | ||||||||||||||||||
self.owner, self.royaltyAmount = self._royaltyInfo(tokenId, msg.value) | ||||||||||||||||||
# make transaction to the contract | ||||||||||||||||||
send(self, self.royaltyAmount) | ||||||||||||||||||
victor-ego marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
{%- endif %} | ||||||||||||||||||
|
||||||||||||||||||
@view | ||||||||||||||||||
@internal | ||||||||||||||||||
def _isApprovedOrOwner(spender: address, tokenId: uint256) -> bool: | ||||||||||||||||||
|
@@ -378,6 +437,10 @@ def transferFrom(owner: address, receiver: address, tokenId: uint256): | |||||||||||||||||
@param receiver The new owner. | ||||||||||||||||||
@param tokenId The NFT to transfer. | ||||||||||||||||||
""" | ||||||||||||||||||
{%- if cookiecutter.force_royalties == 'y' %} | ||||||||||||||||||
# check if royalties have been paid | ||||||||||||||||||
self.royaltyChecker(tokenId) | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can simplify this a bunch! First, gonna change this function
Suggested change
|
||||||||||||||||||
{%- endif %} | ||||||||||||||||||
self._transferFrom(owner, receiver, tokenId, msg.sender) | ||||||||||||||||||
|
||||||||||||||||||
|
||||||||||||||||||
|
@@ -403,6 +466,10 @@ def safeTransferFrom( | |||||||||||||||||
@param tokenId The NFT to transfer. | ||||||||||||||||||
@param data Additional data with no specified format, sent in call to `receiver`. | ||||||||||||||||||
""" | ||||||||||||||||||
{%- if cookiecutter.force_royalties == 'y' %} | ||||||||||||||||||
# check if royalties have been paid | ||||||||||||||||||
self.royaltyChecker(tokenId) | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And here
Suggested change
|
||||||||||||||||||
{%- endif %} | ||||||||||||||||||
self._transferFrom(owner, receiver, tokenId, msg.sender) | ||||||||||||||||||
if receiver.is_contract: # check if `receiver` is a contract address | ||||||||||||||||||
returnValue: bytes4 = ERC721Receiver(receiver).onERC721Received(msg.sender, owner, tokenId, data) | ||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't actually need this!