-
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 20 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,15 @@ event ApprovalForAll: | |||||
operator: indexed(address) | ||||||
approved: bool | ||||||
|
||||||
event PaymentReceived: | ||||||
sender: indexed(address) | ||||||
amount: uint256 | ||||||
|
||||||
event Withdrawal: | ||||||
to: indexed(address) | ||||||
amount: uint256 | ||||||
|
||||||
|
||||||
owner: public(address) | ||||||
isMinter: public(HashMap[address, bool]) | ||||||
|
||||||
|
@@ -109,6 +118,22 @@ 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 minimum amount of royalties that should be paid to transfer a token successfully | ||||||
# NOTE: this can be used to track the floor price for enforcing a minimum royalty payment to the creator | ||||||
# NOTE: current implementation requires paying directly to the smart contract, which forwards to the creator | ||||||
minRoyaltyAmount: public(uint256) | ||||||
|
||||||
# Percentage threshold to decide whether the minRoyaltyAmount should be increased or decreased. | ||||||
thresholdPercentage: public(decimal) | ||||||
|
||||||
lastBalance: public(uint256) | ||||||
|
||||||
totalReceived: public(uint256) | ||||||
|
||||||
payments: HashMap[address, uint256] | ||||||
{%- endif %} | ||||||
|
||||||
{%- if cookiecutter.permitable == 'y' %} | ||||||
############ ERC-4494 ############ | ||||||
|
||||||
|
@@ -165,6 +190,13 @@ def __init__(): | |||||
self.baseURI = "{{cookiecutter.base_uri}}" | ||||||
{%- endif %} | ||||||
|
||||||
# @dev the default value for the minRoyaltyAmount is 0.01 ETH = 10**16 | ||||||
{%- if cookiecutter.force_royalties == 'y' %} | ||||||
self.minRoyaltyAmount = {{ cookiecutter.minRoyaltyAmount }} | ||||||
self.thresholdPercentage = {{ cookiecutter.thresholdPercentage }} | ||||||
self.lastBalance = self.balance | ||||||
{%- endif %} | ||||||
|
||||||
|
||||||
{%- if cookiecutter.permitable == 'y' %} | ||||||
# ERC712 domain separator for ERC4494 | ||||||
|
@@ -179,6 +211,32 @@ def __init__(): | |||||
) | ||||||
{%- endif %} | ||||||
|
||||||
|
||||||
{%- if cookiecutter.force_royalties == 'y' %} | ||||||
@external | ||||||
@payable | ||||||
def __default__(): | ||||||
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. Typically, when smart contracts built by compilers like solidity or vyper send ether to a contract, it only gives a limited amount of gas to that contract to execute. So, basically I think this won't work since there's too much logic going on inside it |
||||||
# Check if the incoming payment is less than the minimum royalty amount | ||||||
if msg.value < self.minRoyaltyAmount: | ||||||
raise "Royalties not paid correctly." | ||||||
|
||||||
# Calculate the expected minimum based on the current minimum royalty amount and the threshold | ||||||
expectedMin: uint256 = self.minRoyaltyAmount * convert(1.0 + self.thresholdPercentage, uint256) | ||||||
|
||||||
# Convert balance to uint256 for comparison | ||||||
balanceInWei: uint256 = self.balance | ||||||
|
||||||
# Check if balance has increased by more than the expected minimum | ||||||
if balanceInWei > self.lastBalance + expectedMin: | ||||||
# Increase minRoyaltyAmount by a certain percentage (for example, by 5%) | ||||||
self.minRoyaltyAmount = convert(convert(self.minRoyaltyAmount, decimal) * 1.05, uint256) | ||||||
elif balanceInWei < self.lastBalance + expectedMin: | ||||||
# Decrease minRoyaltyAmount by a certain percentage (for example, by 5%) | ||||||
self.minRoyaltyAmount = convert(convert(self.minRoyaltyAmount, decimal) * 0.95, uint256) | ||||||
|
||||||
self.lastBalance = balanceInWei | ||||||
{%- endif %} | ||||||
|
||||||
{%- if cookiecutter.metadata == 'y' %} | ||||||
# ERC721 Metadata Extension | ||||||
@pure | ||||||
|
@@ -298,7 +356,21 @@ def royaltyInfo(_tokenId: uint256, _salePrice: uint256) -> (address, uint256): | |||||
""" | ||||||
|
||||||
royalty: uint256 = convert(convert(_salePrice, decimal) * ROYALTY_TO_APPLY_TO_PRICE, uint256) # Percentage that accepts decimals | ||||||
{%- if cookiecutter.force_royalties == 'y' %} | ||||||
return self, max(self.minRoyaltyAmount, royalty) | ||||||
{%- else %} | ||||||
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 %} | ||||||
{%- endif %} | ||||||
|
||||||
{%- if cookiecutter.force_royalties == 'y' %} | ||||||
@external | ||||||
def withdraw(): | ||||||
assert msg.sender == self.owner | ||||||
amount: uint256 = self.balance | ||||||
send(self.owner, amount) | ||||||
log Withdrawal(self.owner, amount) | ||||||
self.lastBalance = self.balance # This should be 0 after the withdrawal | ||||||
{%- endif %} | ||||||
|
||||||
@view | ||||||
|
@@ -325,6 +397,22 @@ def _isApprovedOrOwner(spender: address, tokenId: uint256) -> bool: | |||||
return False | ||||||
|
||||||
|
||||||
# Royality Functions | ||||||
{%- if cookiecutter.force_royalties == 'y' %} | ||||||
@internal | ||||||
def _enforceRoyalties(): | ||||||
# Calculate the payment amount from the most recent transaction | ||||||
payment: uint256 = self.balance - self.lastBalance | ||||||
|
||||||
# Ensure the payment is not less than the minimum royalty amount | ||||||
if payment < self.minRoyaltyAmount: | ||||||
raise "Insufficient payment." | ||||||
|
||||||
# Update the last balance | ||||||
self.lastBalance = self.balance | ||||||
{%- endif %} | ||||||
|
||||||
|
||||||
@internal | ||||||
def _transferFrom(owner: address, receiver: address, tokenId: uint256, sender: address): | ||||||
""" | ||||||
|
@@ -378,6 +466,9 @@ def transferFrom(owner: address, receiver: address, tokenId: uint256): | |||||
@param receiver The new owner. | ||||||
@param tokenId The NFT to transfer. | ||||||
""" | ||||||
{%- if cookiecutter.force_royalties == 'y' %} | ||||||
self._enforceRoyalties() | ||||||
{%- endif %} | ||||||
self._transferFrom(owner, receiver, tokenId, msg.sender) | ||||||
|
||||||
|
||||||
|
@@ -403,6 +494,9 @@ 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' %} | ||||||
self._enforceRoyalties() | ||||||
{%- 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.
Usually files like this are best in your global gitignore
https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files