diff --git a/EIPS/eip-4519.md b/EIPS/eip-4519.md index 8a25ddf698dd0..f12a3a2ece8c2 100644 --- a/EIPS/eip-4519.md +++ b/EIPS/eip-4519.md @@ -4,7 +4,8 @@ title: Non-Fungible Tokens Tied to Physical Assets description: Interface for non-fungible tokens representing physical assets that can generate or recover their own accounts and obey users. author: Javier Arcenegui (@Hardblock-IMSE-CNM), Rosario Arjona (@RosarioArjona), Roberto Román , Iluminada Baturone (@lumi2018) discussions-to: https://ethereum-magicians.org/t/new-proposal-of-smart-non-fungible-token/7677 -status: Review +status: Last Call +last-call-deadline: 2022-11-30 type: Standards Track category: ERC created: 2021-12-03 @@ -45,7 +46,7 @@ $$K_A = SK_A*PK_{O_{-}A}$$ If everything is correctly done, $K_O$ and $K_A$ are the same since: -$$K_O=PK_A*SK_{O_{-}A}=(SK_A*P)\*SK_{O_{-}A}= SK_A*(SK_{O_{-}A}*P)=SK_A*PK_{O_{-}A}$$ +$$K_O=PK_A\*SK_{O_{-}A}=(SK_A\*P)\*SK_{O_{-}A}= SK_A\*(SK_{O_{-}A}\*P)=SK_A\*PK_{O_{-}A}$$ Using the function `ownerEngagement`, the asset sends the hash of $K_A$, and if it is the same as the data in `hashK_OA`, then the state of the token changes to `engagedWithOwner` and the event `OwnerEngaged` are sent. Once the asset receives the event, it changes its operation mode to `engagedWithOwner`. This process is shown in `Figure 4`. From this moment, the asset can be managed by the owner and they can communicate in a secure way using the shared key. @@ -59,7 +60,7 @@ $$K_A = SK_A*PK_{U_{-}A}$$ If everything is correctly done, $K_U$ and $K_A$ are the same since: -$$K_U=PK_A*SK_{U_{-}A}=(SK_A*P)\*SK_{U_{-}A}= SK_A*(SK_{U_{-}A}*P)=SK_A*PK_{U_{-}A}$$ +$$K_U=PK_A\*SK_{U_{-}A}=(SK_A\*P)\*SK_{U_{-}A}= SK_A\*(SK_{U_{-}A}\*P)=SK_A\*PK_{U_{-}A}$$ Using the function `userEngagement`, the asset sends the hash of $K_A$ obtained and if it is the same as the data in `hashK_UA`, then the state of the token changes to `engagedWithUser` and the event `UserEngaged` is sent. Once the asset receives the event, it changes its operation mode to `engagedWithUser`. This process is shown in `Figure 5`. From this moment, the asset can be managed by the user and they can communicate in a secure way using the shared key. diff --git a/EIPS/eip-5298.md b/EIPS/eip-5298.md new file mode 100644 index 0000000000000..19a368e1aa05c --- /dev/null +++ b/EIPS/eip-5298.md @@ -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).