Skip to content

Commit

Permalink
Merge pull request #370 from unstoppabledomains/mike/REG-1449-update-…
Browse files Browse the repository at this point in the history
…enscustody-smart-contract

[REG-1449] feat: Update ENSCustody contract to support wallet migration
  • Loading branch information
nickshatilo authored Oct 17, 2024
2 parents c280ab9 + 272430c commit ad7a194
Show file tree
Hide file tree
Showing 18 changed files with 608 additions and 30 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## v0.9.35

- Added ability to internalTransfer parked domains in `ENSCustody`
- Added `multicall` to ENSCustody

## v0.9.34

- Add `.lfg`, `.dream` Polygon TLD
Expand Down
41 changes: 39 additions & 2 deletions artifacts/ENSCustody.json

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions artifacts/IENSCustody.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,24 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "internalTransfer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
Expand Down
2 changes: 1 addition & 1 deletion artifacts/abi/ENSCustody.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion artifacts/abi/IENSCustody.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"Parked","type":"event"},{"inputs":[{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"name":"commit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"bytes32","name":"secret","type":"bytes32"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"bytes[]","name":"data","type":"bytes[]"},{"internalType":"bool","name":"reverseRecord","type":"bool"},{"internalType":"uint16","name":"ownerControlledFuses","type":"uint16"},{"internalType":"bool","name":"selfCustody","type":"bool"}],"name":"makeCommitment","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"bytes32","name":"secret","type":"bytes32"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"bytes[]","name":"data","type":"bytes[]"},{"internalType":"bool","name":"reverseRecord","type":"bool"},{"internalType":"uint16","name":"ownerControlledFuses","type":"uint16"},{"internalType":"bool","name":"selfCustody","type":"bool"}],"name":"register","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"renew","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"rentPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"Parked","type":"event"},{"inputs":[{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"name":"commit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"internalTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"bytes32","name":"secret","type":"bytes32"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"bytes[]","name":"data","type":"bytes[]"},{"internalType":"bool","name":"reverseRecord","type":"bool"},{"internalType":"uint16","name":"ownerControlledFuses","type":"uint16"},{"internalType":"bool","name":"selfCustody","type":"bool"}],"name":"makeCommitment","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"bytes32","name":"secret","type":"bytes32"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"bytes[]","name":"data","type":"bytes[]"},{"internalType":"bool","name":"reverseRecord","type":"bool"},{"internalType":"uint16","name":"ownerControlledFuses","type":"uint16"},{"internalType":"bool","name":"selfCustody","type":"bool"}],"name":"register","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"renew","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"rentPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
23 changes: 22 additions & 1 deletion contracts/custody/ENSCustody.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {IENSCustody, Unauthorised, InvalidToken, UnknownToken, CustodyNotEnoughB
import {InvalidForwardedToken, ERC2771RegistryContext} from '../metatx/ERC2771RegistryContext.sol';
import {Forwarder} from '../metatx/Forwarder.sol';
import {MinterRole} from '../roles/MinterRole.sol';
import {Multicall} from '../utils/Multicall.sol';

contract ENSCustody is
Initializable,
Expand All @@ -27,10 +28,11 @@ contract ENSCustody is
ERC2771RegistryContext,
Forwarder,
MinterRole,
Multicall,
IENSCustody
{
string public constant NAME = 'ENS Custody';
string public constant VERSION = '0.1.3';
string public constant VERSION = '0.1.4';

bytes32 private constant _ETH_NODE = 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae;
// This is the keccak-256 hash of "ens.owner." subtracted by 1
Expand Down Expand Up @@ -216,14 +218,33 @@ contract ENSCustody is

function safeTransfer(address to, uint256 tokenId) external onlyTokenOwner(tokenId) {
_protectTokenOperation(tokenId);

StorageSlotUpgradeable.getAddressSlot(keccak256(abi.encodePacked(_OWNER_PREFIX_SLOT, tokenId))).value = address(0);

INameWrapper _wrapper = INameWrapper(StorageSlotUpgradeable.getAddressSlot(_ENS_WRAPPER_SLOT).value);

_wrapper.safeTransferFrom(address(this), to, tokenId, 1, '');
}

function internalTransfer(address to, uint256 tokenId) external onlyTokenOwner(tokenId) {
_protectTokenOperation(tokenId);
_park(tokenId, to);
}

receive() external payable {}

function multicall(bytes[] calldata data) public returns (bytes[] memory results) {
bytes[] memory _data = data;

if (isTrustedForwarder(msg.sender)) {
for (uint256 i = 0; i < data.length; i++) {
_data[i] = _buildData(_msgSender(), _msgToken(), data[i], '');
}
}

return _multicall(_data);
}

function _register(
string calldata name,
address owner,
Expand Down
7 changes: 7 additions & 0 deletions contracts/custody/IENSCustody.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ interface IENSCustody is IERC1155ReceiverUpgradeable {
*/
function safeTransfer(address to, uint256 tokenId) external;

/**
* @dev Transfers of internal ownership of a given token ID to another address.
* @param to The address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to transfer of
*/
function internalTransfer(address to, uint256 tokenId) external;

/**
* @dev Fallback function to receive ETH.
*/
Expand Down
1 change: 1 addition & 0 deletions contracts/utils/Multicall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import '@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol';
abstract contract Multicall {
/**
* @dev Receives and executes a batch of function calls on this contract.
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function _multicall(bytes[] memory data) internal returns (bytes[] memory results) {
results = new bytes[](data.length);
Expand Down
2 changes: 1 addition & 1 deletion dist/sandbox/state.json

Large diffs are not rendered by default.

30 changes: 25 additions & 5 deletions dist/types/contracts/custody/ENSCustody.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export declare namespace IForwarder {
};
}
export interface ENSCustodyInterface extends Interface {
getFunction(nameOrSignature: "DEFAULT_ADMIN_ROLE" | "MINTER_ROLE" | "NAME" | "VERSION" | "addMinter" | "addMinters" | "closeMinter" | "commit" | "execute" | "getRoleAdmin" | "grantRole" | "hasRole" | "initialize" | "isMinter" | "isTrustedForwarder" | "makeCommitment" | "nonceOf" | "onERC1155BatchReceived" | "onERC1155Received" | "onERC721Received" | "owner" | "ownerOf" | "register" | "removeMinter" | "removeMinters" | "renew" | "renounceMinter" | "renounceOwnership" | "renounceRole" | "rentPrice" | "revokeRole" | "rotateMinter" | "safeTransfer" | "setBaseRegistrar" | "supportsInterface" | "transferOwnership" | "verify"): FunctionFragment;
getFunction(nameOrSignature: "DEFAULT_ADMIN_ROLE" | "MINTER_ROLE" | "NAME" | "VERSION" | "addMinter" | "addMinters" | "closeMinter" | "commit" | "execute" | "getRoleAdmin" | "grantRole" | "hasRole" | "initialize" | "isMinter" | "isTrustedForwarder" | "makeCommitment" | "multicall" | "nonceOf" | "onERC1155BatchReceived" | "onERC1155Received" | "onERC721Received" | "owner" | "ownerOf" | "register" | "removeMinter" | "removeMinters" | "renew" | "renounceMinter" | "renounceOwnership" | "renounceRole" | "rentPrice" | "revokeRole" | "rotateMinter" | "safeTransfer(address,uint256,bool)" | "safeTransfer(address,uint256)" | "setBaseRegistrar" | "supportsInterface" | "transferOwnership" | "verify"): FunctionFragment;
getEvent(nameOrSignatureOrTopic: "Initialized" | "OwnershipTransferred" | "Parked" | "RoleAdminChanged" | "RoleGranted" | "RoleRevoked"): EventFragment;
encodeFunctionData(functionFragment: "DEFAULT_ADMIN_ROLE", values?: undefined): string;
encodeFunctionData(functionFragment: "MINTER_ROLE", values?: undefined): string;
Expand Down Expand Up @@ -48,6 +48,7 @@ export interface ENSCustodyInterface extends Interface {
BigNumberish,
boolean
]): string;
encodeFunctionData(functionFragment: "multicall", values: [BytesLike[]]): string;
encodeFunctionData(functionFragment: "nonceOf", values: [BigNumberish]): string;
encodeFunctionData(functionFragment: "onERC1155BatchReceived", values: [
AddressLike,
Expand Down Expand Up @@ -80,7 +81,8 @@ export interface ENSCustodyInterface extends Interface {
encodeFunctionData(functionFragment: "rentPrice", values: [string, BigNumberish]): string;
encodeFunctionData(functionFragment: "revokeRole", values: [BytesLike, AddressLike]): string;
encodeFunctionData(functionFragment: "rotateMinter", values: [AddressLike]): string;
encodeFunctionData(functionFragment: "safeTransfer", values: [AddressLike, BigNumberish]): string;
encodeFunctionData(functionFragment: "safeTransfer(address,uint256,bool)", values: [AddressLike, BigNumberish, boolean]): string;
encodeFunctionData(functionFragment: "safeTransfer(address,uint256)", values: [AddressLike, BigNumberish]): string;
encodeFunctionData(functionFragment: "setBaseRegistrar", values: [AddressLike]): string;
encodeFunctionData(functionFragment: "supportsInterface", values: [BytesLike]): string;
encodeFunctionData(functionFragment: "transferOwnership", values: [AddressLike]): string;
Expand All @@ -101,6 +103,7 @@ export interface ENSCustodyInterface extends Interface {
decodeFunctionResult(functionFragment: "isMinter", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "isTrustedForwarder", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "makeCommitment", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "multicall", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "nonceOf", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "onERC1155BatchReceived", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "onERC1155Received", data: BytesLike): Result;
Expand All @@ -117,7 +120,8 @@ export interface ENSCustodyInterface extends Interface {
decodeFunctionResult(functionFragment: "rentPrice", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "revokeRole", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "rotateMinter", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "safeTransfer", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "safeTransfer(address,uint256,bool)", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "safeTransfer(address,uint256)", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "setBaseRegistrar", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "supportsInterface", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "transferOwnership", data: BytesLike): Result;
Expand Down Expand Up @@ -283,6 +287,7 @@ export interface ENSCustody extends BaseContract {
], [
string
], "view">;
multicall: TypedContractMethod<[data: BytesLike[]], [string[]], "nonpayable">;
nonceOf: TypedContractMethod<[tokenId: BigNumberish], [bigint], "view">;
onERC1155BatchReceived: TypedContractMethod<[
arg0: AddressLike,
Expand Down Expand Up @@ -362,7 +367,14 @@ export interface ENSCustody extends BaseContract {
void
], "nonpayable">;
rotateMinter: TypedContractMethod<[receiver: AddressLike], [void], "payable">;
safeTransfer: TypedContractMethod<[
"safeTransfer(address,uint256,bool)": TypedContractMethod<[
to: AddressLike,
tokenId: BigNumberish,
internalTransfer: boolean
], [
void
], "nonpayable">;
"safeTransfer(address,uint256)": TypedContractMethod<[
to: AddressLike,
tokenId: BigNumberish
], [
Expand Down Expand Up @@ -439,6 +451,7 @@ export interface ENSCustody extends BaseContract {
], [
string
], "view">;
getFunction(nameOrSignature: "multicall"): TypedContractMethod<[data: BytesLike[]], [string[]], "nonpayable">;
getFunction(nameOrSignature: "nonceOf"): TypedContractMethod<[tokenId: BigNumberish], [bigint], "view">;
getFunction(nameOrSignature: "onERC1155BatchReceived"): TypedContractMethod<[
arg0: AddressLike,
Expand Down Expand Up @@ -510,7 +523,14 @@ export interface ENSCustody extends BaseContract {
void
], "nonpayable">;
getFunction(nameOrSignature: "rotateMinter"): TypedContractMethod<[receiver: AddressLike], [void], "payable">;
getFunction(nameOrSignature: "safeTransfer"): TypedContractMethod<[
getFunction(nameOrSignature: "safeTransfer(address,uint256,bool)"): TypedContractMethod<[
to: AddressLike,
tokenId: BigNumberish,
internalTransfer: boolean
], [
void
], "nonpayable">;
getFunction(nameOrSignature: "safeTransfer(address,uint256)"): TypedContractMethod<[
to: AddressLike,
tokenId: BigNumberish
], [
Expand Down
2 changes: 1 addition & 1 deletion dist/types/contracts/custody/ENSCustody.d.ts.map

Large diffs are not rendered by default.

34 changes: 33 additions & 1 deletion dist/types/factories/contracts/custody/ENSCustody__factory.d.ts

Large diffs are not rendered by default.

Loading

0 comments on commit ad7a194

Please sign in to comment.