forked from ethereum/EIPs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add EIP-5298: ENS as Token Holder (ethereum#5300)
* Create eip-ens-as-holder * Update eip-ens-as-holder * Update eip-ens-as-holder * Update eip-ens-as-holder * Update eip-ens-as-holder * Update eip-ens-as-holder * Update eip-ens-as-holder * Update EIPS/eip-ens-as-holder Co-authored-by: Micah Zoltu <[email protected]> * Update EIPS/eip-ens-as-holder Co-authored-by: Pandapip1 <[email protected]> * Update EIPS/eip-ens-as-holder Co-authored-by: Pandapip1 <[email protected]> * Update EIPS/eip-ens-as-holder Co-authored-by: Pandapip1 <[email protected]> * Update EIPS/eip-ens-as-holder Co-authored-by: Pandapip1 <[email protected]> * Update EIPS/eip-ens-as-holder Co-authored-by: Pandapip1 <[email protected]> * Update EIPS/eip-ens-as-holder Co-authored-by: Pandapip1 <[email protected]> * Update EIPS/eip-ens-as-holder Co-authored-by: Pandapip1 <[email protected]> * Update content * Update content * Update content * Update content * Add reference implementations and test cases * Update spacing * Fix spacing * Change * fix format * Update lint * Update wording * Fix typos Co-authored-by: Micah Zoltu <[email protected]> Co-authored-by: Pandapip1 <[email protected]>
- Loading branch information
1 parent
b38791b
commit f5d4139
Showing
1 changed file
with
149 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
--- | ||
eip: 5298 | ||
title: ENS Trust to hold NFTs under ENS name | ||
description: An interface for a smart contract acting as a "trust" that holds tokens by ENS name. | ||
author: Zainan Victor Zhou (@xinbenlv) | ||
discussions-to: https://ethereum-magicians.org/t/erc-eip-5198-ens-as-token-holder/10374 | ||
status: Draft | ||
type: Standards Track | ||
category: ERC | ||
created: 2022-07-12 | ||
requires: 137, 721, 1155 | ||
--- | ||
|
||
## Abstract | ||
|
||
This EIP standardizes an interface for smart contracts to hold of [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md) tokens on behalf of ENS domains. | ||
|
||
## Motivation | ||
|
||
Currently, if someone wants to receive a token, they have to set up a wallet address. This EIP decouples NFT ownership from wallet addresses. | ||
|
||
## Specification | ||
|
||
1. Compliant contracts MUST implement `ERC721TokenReceiver`, as defined in [EIP-721](./eip-721.md). | ||
2. Compliant contracts implement the following interface: | ||
|
||
```solidity | ||
interface IERC_ENS_TRUST is ERC721Receiver, ERC1155Receiver { | ||
function claimTo(address to, bytes32 ensNode, address operator, uint256 tokenId) payable external; | ||
} | ||
``` | ||
|
||
3. `claimTo` MUST check if `msg.sender` is the owner of the ENS node (and/or approved by the domain in implementation-specific ways). The compliant contract then MUST make a call to the `safeTransferFrom` function of [EIP-721](./eip-712.md) or [EIP-1155](./eip-1155.md). | ||
|
||
## Rationale | ||
|
||
1. ENS was chosen because it is a well-established scoped ownership namespace. | ||
This is nonetheless compatible with other scoped ownership namespaces. | ||
|
||
## Backwards Compatibility | ||
|
||
No backward compatibility issues were found. | ||
|
||
## Test Cases | ||
|
||
```ts | ||
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; | ||
import { expect } from "chai"; | ||
import { ethers } from "hardhat"; | ||
|
||
describe("FirstENSBankAndTrust", function () { | ||
|
||
describe("Receive and Claim Token", function () { | ||
|
||
it("Should ACCEPT/REJECT claimTo based on if ENS owner is msg.sender", async function () { | ||
... | ||
// Steps of testing: | ||
// mint to charlie | ||
// charlie send to ENSTrust and recorded under bob.xinbenlvethsf.eth | ||
// bob try to claimTo alice, first time it should be rejected | ||
// bob then set the ENS record | ||
// bob claim to alice, second time it should be accepted | ||
|
||
// mint to charlie | ||
await erc721ForTesting.mint(charlie.address, fakeTokenId); | ||
|
||
// charlie send to ENSTrust and recorded under bob.xinbenlvethsf.eth | ||
await erc721ForTesting.connect(charlie)["safeTransferFrom(address,address,uint256,bytes)"]( | ||
charlie.address, firstENSBankAndTrust.address, | ||
fakeTokenId, | ||
fakeReceiverENSNamehash | ||
); | ||
|
||
// bob try to claimTo alice, first time it should be rejected | ||
await expect(firstENSBankAndTrust.connect(bob).claimTo( | ||
alice.address, | ||
fakeReceiverENSNamehash, | ||
firstENSBankAndTrust.address, | ||
fakeTokenId | ||
)) | ||
.to.be.rejectedWith("ENSTokenHolder: node not owned by sender"); | ||
|
||
// bob then set the ENS record | ||
await ensForTesting.setOwner( | ||
fakeReceiverENSNamehash, bob.address | ||
); | ||
|
||
// bob claim to alice, second time it should be accepted | ||
await expect(firstENSBankAndTrust.connect(bob).claimTo( | ||
alice.address, | ||
fakeReceiverENSNamehash, | ||
erc721ForTesting.address, | ||
fakeTokenId | ||
)); | ||
}); | ||
}); | ||
}); | ||
``` | ||
|
||
## Reference Implementation | ||
|
||
```solidity | ||
pragma solidity ^0.8.9; | ||
contract FirstENSBankAndTrust is IERC721Receiver, Ownable { | ||
function getENS() public view returns (ENS) { | ||
return ENS(ensAddress); | ||
} | ||
function setENS(address newENSAddress) public onlyOwner { | ||
ensAddress = newENSAddress; | ||
} | ||
// @dev This function is called by the owner of the token to approve the transfer of the token | ||
// @param data MUST BE the ENS node of the intended token receiver this ENSHoldingServiceForNFT is holding on behalf of. | ||
function onERC721Received( | ||
address operator, | ||
address /*from*/, | ||
uint256 tokenId, | ||
bytes calldata data | ||
) external override returns (bytes4) { | ||
require(data.length == 32, "ENSTokenHolder: last data field must be ENS node."); | ||
// --- START WARNING --- | ||
// DO NOT USE THIS IN PROD | ||
// this is just a demo purpose of using extraData for node information | ||
// In prod, you should use a struct to store the data. struct should clearly identify the data is for ENS | ||
// rather than anything else. | ||
bytes32 ensNode = bytes32(data[0:32]); | ||
// --- END OF WARNING --- | ||
addToHolding(ensNode, operator, tokenId); // conduct the book keeping | ||
return ERC721_RECEIVER_MAGICWORD; | ||
} | ||
function claimTo(address to, bytes32 ensNode, address tokenContract uint256 tokenId) public { | ||
require(getENS().owner(ensNode) == msg.sender, "ENSTokenHolder: node not owned by sender"); | ||
removeFromHolding(ensNode, tokenContract, tokenId); | ||
IERC721(tokenContract).safeTransferFrom(address(this), to, tokenId); | ||
} | ||
} | ||
``` | ||
|
||
## Security Considerations | ||
|
||
Needs discussion. | ||
|
||
## Copyright | ||
|
||
Copyright and related rights waived via [CC0](../LICENSE.md). |