Add EIP-5320: Harberger Taxes NFT#5320
Conversation
|
Hi! I'm a bot, and I wanted to automerge your PR, but couldn't because of the following issue(s): (fail) eip-5320.md
|
|
The commit bffc802 (as a parent of 213ef96) contains errors. Please inspect the Run Summary for details. |
|
The commit e066be5 (as a parent of 213ef96) contains errors. Please inspect the Run Summary for details. |
MicahZoltu
left a comment
There was a problem hiding this comment.
Make sure you address all of the feedback from the bot (see inline comments in files tab in GitHub), and address any CI failures that appear to be problems with the EIP.
|
The commit 6d8c9bd (as a parent of 213ef96) contains errors. Please inspect the Run Summary for details. |
|
|
||
| ### EIP-20 compatibility | ||
|
|
||
| EIP-20 compliant tokens are a widespread industry standard and using native tokens is limiting. |
There was a problem hiding this comment.
One caveat: what if someone makes an EIP-20 token, gives themselves all of it, and then values the NFT at 1 base unit of that token?
There was a problem hiding this comment.
That's for the Harberger Tax contract to decide. The deployer can define what the allowed currency is. For example, in this implementation the currency is defined at construction time, so you can, for example, make the allowed currency be WETH.
Having a single, enforced currency is not enforced at the standard level, but ideally a contract shouldn't allow any EIP-20 token because it opens up these attacks.
There was a problem hiding this comment.
Could you put this in the Security Considerations?
There was a problem hiding this comment.
HT may be philosophically incompatible with fungibility because each token, by nature of having a self-assessed valuation, is non-fungible.
Co-authored-by: Pandapip1 <45835846+Pandapip1@users.noreply.github.com>
Co-authored-by: Pandapip1 <45835846+Pandapip1@users.noreply.github.com>
|
The commit bbccb8f (as a parent of b81b7e4) contains errors. Please inspect the Run Summary for details. |
| /// @dev call this to change the perceived valuation of the token. | ||
| /// @param _tokenId The identifier of the token to change valuation of. | ||
| /// @param _valuation The new valuation of the token. | ||
| function changeValuation(uint256 _tokenId, uint256 _valuation) external; |
There was a problem hiding this comment.
since there's no "tax period", it's hard to know when taxes must be paid for someone who owns this token. I was working with CityDAO on this a proposal for using Harberger taxes on a parcel of land, and one of the things that folks need to know is when the collection period is. You don't want to make it a single date because then folks will try to game the system, and you don't want to make it "all year long" because you don't know when it will actually be assessed.
I wrote up quite a bit of detail here if you want to take a look. It may inform this EIP in some ways to make tax collection more standardized.
There was a problem hiding this comment.
@mdnorman This comment would be better suited for the discussions-to link so it doesn't get lost. Comments on the PR itself should be limited to editorial type things, while comments on the content itself should go in the discussions-to. PRs are very transient in nature, while the discussion link is much more long lived.
There was a problem hiding this comment.
Thanks. First timer commenting on an EIP. I've copied my commentary to the discussion.
| /// @dev buy a token. This function must succeed if | ||
| /// @param _tokenId The identifier of the token to buy. | ||
| /// @param _offer The maximum amount offered to buy this token. | ||
| /// @param _valuation The valuation the token will have after a successful purchase. | ||
| /// @param _fund The fund put in to pay for the taxes of the token. | ||
| function buy(uint256 _tokenId, uint256 _offer, uint256 _valuation, uint256 _fund) external; |
There was a problem hiding this comment.
The specification should make it explicit that when this function is called, collect must be called internally before the sale completes.
| /// @dev call this to manually send pending taxes to the collector. | ||
| /// This function should be callable by any external party. | ||
| /// @param _tokenId The identifier of the token to pay taxes for. | ||
| function collect(uint256 _tokenId) external; |
There was a problem hiding this comment.
The specification should assert what happens when collect fails for any reason. Presumably the remaining funds would be collected and the NFT's valuation would be set to 0 (which would allow anyone to acquire the property for free)?
There was a problem hiding this comment.
Presumably the remaining funds would be collected and the NFT's valuation would be set to 0 (which would allow anyone to acquire the property for free)?
Shouldn't this transfer the token to the collector, not set the valuation to zero? The collector is owed taxes, but didn't receive them.
| /// @dev call this to revoke ownership of a token. | ||
| /// @param _tokenId The identifier of the token to revoke. | ||
| function revoke(uint256 _tokenId) external; |
There was a problem hiding this comment.
This feels unnecessary. The owner can just set the valuation to 0, which would make the property free to acquire by anyone.
| /// @dev call this to remove excess funding for the token. | ||
| /// if the resultant fund for the token was zero, it must be revoked. | ||
| /// @param _tokenId The identifier of the token to defund. | ||
| /// @param _value The amount to retrieve from the token's fund. | ||
| function defund(uint256 _tokenId, uint256 _value) external; |
There was a problem hiding this comment.
This should probably assert that the valuation gets set to 0 as a side effect of defunding.
| /// @dev call this to change the perceived valuation of the token. | ||
| /// @param _tokenId The identifier of the token to change valuation of. | ||
| /// @param _valuation The new valuation of the token. | ||
| function changeValuation(uint256 _tokenId, uint256 _valuation) external; |
There was a problem hiding this comment.
Security Considerations section should mention that collect SHOULD be called internally from this method prior to changing the valuation to avoid tax evasion.
|
|
||
| ### Taxes and funds are per token, not per account | ||
|
|
||
| This made for a simpler implementation, but it may be too naive. Please give feedback on how to implement this. |
There was a problem hiding this comment.
Still need to go through this more and give my thoughts (at a retreat this week, so might have to wait).
But at https://wildcards.world we had a system where users could share deposits between their harberger NFTs, and the harberger tax of those NFTs could go to multiple different beneficiaries. (hence it is "scalable" for users to own multiple NFTs that share a deposit and fund multiple beneficiaries).
I gave a presentation on this a while ago which can be found here: https://youtu.be/LYm-cVuo0sg and here is the slideshow: Harberger Tax Contract Overview.pdf
Here are those contracts, which might be an interesting reference.
I believe use-cases are limited where there isn't this kind of flexibility, and it would be sad if the contract interface was drastically different between them.
A fun and enlightening task (no promise on timeline) would be to see if I can refactor the Wildcards contracts to be compliant with this EIP 👍
| /// @param _owner The owner of the token after the purchase. | ||
| /// if equal to address(0), it means the token was revoked. | ||
| /// @param _amount The amount that was paid in this transaction. | ||
| event TokenBought(uint256 indexed _tokenId, address indexed _owner, uint256 _amount); |
There was a problem hiding this comment.
"Lease" and "Leasor" are a better fit here; one of the core ideas of HT is that an asset within this system cannot be "owed" (as commonly understood within the prevailing contexts of private ownership).
| /// fund was updated. | ||
| /// @param _tokenId The identifier of the token that had a fund change. | ||
| /// @param _amount The updated amount of the fund. | ||
| event TokenFunded(uint256 indexed _tokenId, uint256 _amount); |
There was a problem hiding this comment.
The standard should not assume that a deposit model is necessary. Deposit vs. non-deposit are alternative methods with their own respective tradeoffs (which should be documented as part of this standard).
EIPS/eip-5320.md
Outdated
| The main obvious case of Harberger Taxes is to be used as a coordination mechanism for land allocation, but, to give some examples: | ||
|
|
||
| - Real-world land allocation. | ||
| - Digital land allocation (videogames, social experiments...). | ||
| - Advertisement Space in websites. | ||
| - Governance mechanisms that use "seats" subject to Harberger. | ||
| - Social media that requires holding a token to interact. | ||
|
|
There was a problem hiding this comment.
These use cases can go in the Motivation section. They don't belong in a technical abstract.
| /// @param _value The taxes that have been paid. | ||
| event TaxPaid(uint256 indexed _tokenId, uint256 _value); | ||
|
|
||
| /// @dev buy a token. This function must succeed if |
There was a problem hiding this comment.
Might want to finish this sentence?
| /// @dev call this to manually send pending taxes to the collector. | ||
| /// This function should be callable by any external party. | ||
| /// @param _tokenId The identifier of the token to pay taxes for. | ||
| function collect(uint256 _tokenId) external; |
There was a problem hiding this comment.
Presumably the remaining funds would be collected and the NFT's valuation would be set to 0 (which would allow anyone to acquire the property for free)?
Shouldn't this transfer the token to the collector, not set the valuation to zero? The collector is owed taxes, but didn't receive them.
Co-authored-by: Sam Wilson <57262657+SamWilsn@users.noreply.github.com> Co-authored-by: Micah Zoltu <micah@zoltu.net>
|
There has been no activity on this pull request for 2 weeks. It will be closed after 3 months of inactivity. If you would like to move this PR forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review. |
| /// @dev call this to fund the item, to pay for the maintenance taxes. | ||
| /// @param _tokenId The identifier of the token to fund. | ||
| /// @param _value The amount sent to fund the token. | ||
| function fund(uint256 _tokenId, uint256 _value) external; |
There was a problem hiding this comment.
Which amount of token is this _value here representing?
|
There has been no activity on this pull request for 2 weeks. It will be closed after 3 months of inactivity. If you would like to move this PR forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review. |
| /// if the resultant fund for the token was zero, it must be revoked. | ||
| /// @param _tokenId The identifier of the token to defund. | ||
| /// @param _value The amount to retrieve from the token's fund. | ||
| function defund(uint256 _tokenId, uint256 _value) external; |
There was a problem hiding this comment.
Do we have to have this function in the standard? If yes, then where exactly should this function be used and what is the benefit of having defunding available for users?
|
There has been no activity on this pull request for 2 weeks. It will be closed after 3 months of inactivity. If you would like to move this PR forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review. |
|
This pull request was closed due to inactivity. If you are still pursuing it, feel free to reopen it and respond to any feedback or request a review in a comment. |
No description provided.