Skip to content

Commit 473d6a0

Browse files
authoredMar 21, 2025··
Update ERC-7656: Expand the scope of the proposal
Merged by EIP-Bot.
1 parent 963570f commit 473d6a0

12 files changed

+589
-220
lines changed
 

‎ERCS/erc-7656.md

+167-104
Large diffs are not rendered by default.

‎assets/erc-7656/ERC7656Factory.sol

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
import {IERC7656Factory} from "./interfaces/IERC7656Factory.sol";
5+
import {IERC165} from "./interfaces/IERC165.sol";
6+
7+
import {ERC7656BytecodeLib} from "./lib/ERC7656BytecodeLib.sol";
8+
9+
contract ERC7656Factory is IERC165, IERC7656Factory {
10+
/**
11+
* @dev Creates a proxy contract using the provided parameters
12+
* If the proxy already exists, returns its address without attempting creation
13+
*/
14+
function create(
15+
address implementation,
16+
bytes32 salt,
17+
uint256 chainId,
18+
bytes12 mode,
19+
address linkedContract,
20+
uint256 linkedId
21+
) external returns (address) {
22+
bytes memory bytecode = ERC7656BytecodeLib.getCreationCode(implementation, salt, chainId, mode, linkedContract, linkedId);
23+
address computedAddress = ERC7656BytecodeLib.computeAddress(salt, keccak256(bytecode), address(this));
24+
uint256 size;
25+
// solhint-disable-next-line no-inline-assembly
26+
assembly {
27+
size := extcodesize(computedAddress)
28+
}
29+
if (size == 0) {
30+
// solhint-disable-next-line no-inline-assembly
31+
assembly {
32+
// If the service has not yet been deployed
33+
let deployed := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
34+
35+
// Revert if the deployment fails
36+
if iszero(deployed) {
37+
mstore(0x00, 0xd786d393) // `CreationFailed()`
38+
revert(0x1c, 0x04)
39+
}
40+
}
41+
emit Created(computedAddress, implementation, salt, chainId, mode, linkedContract, linkedId);
42+
}
43+
return computedAddress;
44+
}
45+
46+
/**
47+
* @dev Computes the address where the proxy will be deployed
48+
*/
49+
function compute(
50+
address implementation,
51+
bytes32 salt,
52+
uint256 chainId,
53+
bytes12 mode,
54+
address linkedContract,
55+
uint256 linkedId
56+
) external view returns (address) {
57+
bytes memory bytecode = ERC7656BytecodeLib.getCreationCode(implementation, salt, chainId, mode, linkedContract, linkedId);
58+
return ERC7656BytecodeLib.computeAddress(salt, keccak256(bytecode), address(this));
59+
}
60+
61+
/// @dev Returns true if interfaceId is IERC7656Factory.sol's interfaceId
62+
function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
63+
return interfaceId == type(IERC7656Factory).interfaceId || interfaceId == type(IERC165).interfaceId;
64+
}
65+
}

‎assets/erc-7656/ERC7656Registry.sol

-116
This file was deleted.

‎assets/erc-7656/ERC7656Service.sol

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: GPL3
2+
pragma solidity ^0.8.20;
3+
4+
import {IERC165} from "./interfaces/IERC165.sol";
5+
import {IERC7656Service} from "./interfaces/IERC7656Service.sol";
6+
7+
import {ERC7656ServiceLib} from "./lib/ERC7656ServiceLib.sol";
8+
9+
/**
10+
* @title ERC7656Service.sol
11+
*/
12+
contract ERC7656Service is IERC7656Service, IERC165 {
13+
function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
14+
return interfaceId == type(IERC7656Service).interfaceId || interfaceId == type(IERC165).interfaceId;
15+
}
16+
17+
/**
18+
* @notice Returns the linkedContract linked to the contract
19+
*/
20+
function linkedData() public view virtual override returns (uint256, bytes12, address, uint256) {
21+
return _linkedData();
22+
}
23+
24+
/**
25+
* Private functions
26+
*/
27+
28+
function _linkedData() internal view returns (uint256, bytes12, address, uint256) {
29+
return ERC7656ServiceLib.linkedData(address(this));
30+
}
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.20;
3+
4+
import {ERC7656Service} from "../ERC7656Service.sol";
5+
6+
contract NFTRevenueSharing is ERC7656Service {
7+
address[] public beneficiaries;
8+
uint256[] public shares;
9+
uint256 public totalShares;
10+
11+
function initialize(address[] memory _beneficiaries, uint256[] memory _shares) external {
12+
// Get linked data to verify caller is the NFT owner
13+
(uint256 chainId, bytes12 mode, address nftContract, uint256 tokenId) = _linkedData();
14+
require(chainId == block.chainid, "Wrong chain");
15+
require(mode == 0x000000000000000000000000, "Wrong mode");
16+
17+
// Verify caller is the NFT owner
18+
address owner = IERC721(nftContract).ownerOf(tokenId);
19+
require(msg.sender == owner, "Not token owner");
20+
21+
// Initialize revenue sharing parameters
22+
beneficiaries = _beneficiaries;
23+
shares = _shares;
24+
25+
for (uint i = 0; i < _shares.length; i++) {
26+
totalShares += _shares[i];
27+
}
28+
}
29+
30+
// Implement revenue distribution logic...
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.10;
3+
4+
import {ERC7656Service} from "../ERC7656Service.sol";
5+
6+
contract RWAComplianceService is ERC7656Service {
7+
// Compliance status
8+
mapping(address => bool) public isWhitelisted;
9+
bool public transfersRestricted;
10+
address public complianceManager;
11+
12+
// Legal documentation
13+
string public legalDocumentationURI;
14+
bytes32 public legalDocumentationHash;
15+
16+
function initialize(address _complianceManager, string memory _legalDocumentationURI, bytes32 _legalDocumentationHash) external {
17+
// Get linked data to verify caller is the NFT owner
18+
(uint256 chainId, bytes12 mode, address nftContract, uint256 tokenId) = _linkedData();
19+
require(chainId == block.chainid, "Wrong chain");
20+
require(mode == 0x000000000000000000000000, "Wrong mode");
21+
22+
// Verify caller is the NFT owner
23+
address owner = IERC721(nftContract).ownerOf(tokenId);
24+
require(msg.sender == owner, "Not token owner");
25+
26+
// Initialize RWA compliance parameters
27+
complianceManager = _complianceManager;
28+
legalDocumentationURI = _legalDocumentationURI;
29+
legalDocumentationHash = _legalDocumentationHash;
30+
transfersRestricted = true;
31+
32+
// Whitelist the current owner
33+
isWhitelisted[owner] = true;
34+
}
35+
36+
// Compliance and regulatory reporting functions...
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.10;
3+
4+
import {ERC7656Service} from "../ERC7656Service.sol";
5+
6+
contract SocialRecoveryService is ERC7656Service {
7+
// Guardians who can initiate recovery
8+
mapping(address => bool) public guardians;
9+
uint256 public guardiansCount;
10+
uint256 public threshold;
11+
12+
// Recovery request data
13+
address public pendingOwner;
14+
mapping(address => bool) public hasApproved;
15+
uint256 public approvalsCount;
16+
17+
function initialize(address[] memory _guardians, uint256 _threshold) external {
18+
// Get linked data to verify caller is the account owner
19+
(uint256 chainId, bytes12 mode, address account, ) = _linkedData();
20+
require(chainId == block.chainid, "Wrong chain");
21+
require(mode == 0x000000000000000000000001, "Wrong mode");
22+
23+
// Verify caller is the linked account
24+
require(msg.sender == account, "Not authorized");
25+
26+
// Initialize recovery parameters
27+
threshold = _threshold;
28+
for (uint i = 0; i < _guardians.length; i++) {
29+
guardians[_guardians[i]] = true;
30+
}
31+
guardiansCount = _guardians.length;
32+
}
33+
34+
// Implement recovery logic...
35+
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
/**
5+
* @title ERC7656
6+
* @dev Modified registry based on ERC6551Registry
7+
* https://github.com/erc6551/reference/blob/main/src/ERC6551Registry.sol
8+
*
9+
* The ERC165 interfaceId is 0xc6bdc908
10+
* @notice Manages the creation of token linked services
11+
*/
12+
interface IERC7656Factory {
13+
/**
14+
* @notice The registry MUST emit the Created event upon successful contract creation.
15+
* @param contractAddress The address of the created contract
16+
* @param implementation The address of the implementation contract
17+
* @param chainId The chain linkedId of the chain where the contract is being created
18+
* @param linkedContract The address of the token or contract
19+
* @param linkedId The optional ID (e.g., linkedId) of the linked contract, or 0 if not applicable
20+
* @param salt The salt is a bytes32 so save 32 bytes in the bytecode. It is converted in the function to a bytes32 to use it for the create2 operation
21+
* @param mode If true, the linkedId is not used, saving 32 bytes in the bytecode
22+
*/
23+
event Created(
24+
address contractAddress,
25+
address indexed implementation,
26+
bytes32 salt,
27+
uint256 chainId,
28+
bytes12 mode,
29+
address indexed linkedContract,
30+
uint256 indexed linkedId
31+
);
32+
33+
/**
34+
* The registry MUST revert with CreationFailed error if the create2 operation fails.
35+
*/
36+
error CreationFailed();
37+
38+
/**
39+
* @notice Creates a token or contract-linked service.
40+
* If the service has already been created, returns the service address without calling create2.
41+
* @param implementation The address of the implementation contract
42+
* @param chainId The chain linkedId of the chain where the service is being created
43+
* @param linkedContract The address of the token or contract
44+
* @param linkedId The optional ID (e.g., linkedId) of the linked contract. If mode is true, this value is ignored
45+
* @param salt The salt is a bytes32 so save 32 bytes in the bytecode. It is converted in the function to a bytes32 to use it for the create2 operation
46+
* @param mode If true, the linkedId is not used, saving 32 bytes in the bytecode
47+
* @return service The address of the token or contract-linked service
48+
*/
49+
function create(
50+
address implementation,
51+
bytes32 salt,
52+
uint256 chainId,
53+
bytes12 mode,
54+
address linkedContract,
55+
uint256 linkedId
56+
) external returns (address);
57+
58+
/**
59+
* @notice Returns the computed token or contract-linked service address.
60+
* @param implementation The address of the implementation contract
61+
* @param salt The salt to use for the create2 operation
62+
* @param chainId The chain linkedId of the chain where the service is being created
63+
* @param linkedContract The address of the token or contract
64+
* @param linkedId The optional ID (e.g., linkedId) of the linked contract. If mode is true, this value is ignored
65+
* @param mode If true, the linkedId is not used, saving 32 bytes in the bytecode
66+
* @return service The address of the token or contract-linked service
67+
*/
68+
function compute(
69+
address implementation,
70+
bytes32 salt,
71+
uint256 chainId,
72+
bytes12 mode,
73+
address linkedContract,
74+
uint256 linkedId
75+
) external view returns (address service);
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
/**
5+
* @title IERC7656Service.sol.sol.sol
6+
* InterfaceId 0xfc0c546a
7+
*/
8+
interface IERC7656Service {
9+
/**
10+
* @notice Returns the token linked to the contract
11+
* @return chainId The chainId where the linked contract is deployed
12+
* @return mode The mode of the link (with or without linkedId)
13+
* @return linkedContract The address of the linked contract
14+
* @return linkedId The id of the linked contract (for example, a tokenId)
15+
*/
16+
function linkedData() external view returns (uint256 chainId, bytes12 mode, address linkedContract, uint256 linkedId);
17+
}
+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.4;
3+
4+
// This implementation is a variation of https://github.com/erc6551/reference/blob/main/src/lib/ERC6551BytecodeLib.sol
5+
// Modified by: Francesco Sullo @sullof
6+
7+
library ERC7656BytecodeLib {
8+
/**
9+
* @dev Returns the creation code of a service linked to a contract
10+
* @return result The creation code of the linked service
11+
*/
12+
function getCreationCode(
13+
address implementation,
14+
bytes32 salt,
15+
uint256 chainId,
16+
bytes12 mode,
17+
address linkedContract,
18+
uint256 linkedId
19+
) internal pure returns (bytes memory result) {
20+
// solhint-disable-next-line no-inline-assembly
21+
assembly {
22+
// data structure
23+
// 0x14: erc1167 header (10 bytes)
24+
// 0x28: implementation (20 bytes)
25+
// 0x37: erc1167 footer (15 bytes)
26+
// 0x77: salt (32 bytes)
27+
// 0x57: chainId (32 bytes)
28+
// 0x97 mode (1 byte)
29+
// 0x98 empty space (11 bytes)
30+
// 0xa9: linkedContract (20 bytes)
31+
// 0xb7: linkedId (32 bytes, optional)
32+
33+
result := mload(0x40) // Grab the free memory pointer
34+
mstore(add(result, 0x37), 0x5af43d82803e903d91602b57fd5bf3) // erc1167 footer
35+
mstore(add(result, 0x28), implementation)
36+
mstore(add(result, 0x14), 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73)
37+
mstore(add(result, 0x57), salt)
38+
mstore(add(result, 0x77), chainId) //
39+
mstore(add(result, 0x97), or(mode, linkedContract)) //
40+
mstore(add(result, 0xb7), linkedId)
41+
mstore(result, 0xb7) // Store the length
42+
mstore(0x40, add(result, 0xd7)) // Allocate the memory
43+
}
44+
}
45+
46+
function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address result) {
47+
// In YUL, a bytes32 salt is passed as a right-padded bytes32. For example, if we
48+
// have salt = 0x1234567890123456789012, it is passed in YUL as
49+
// 0x1234567890123456789012000000000000000000000000000000000000000000
50+
// and this way it is used in the create2 opcode.
51+
// solhint-disable-next-line no-inline-assembly
52+
assembly {
53+
result := mload(0x40) // Grab the free memory pointer
54+
mstore8(result, 0xff)
55+
mstore(add(result, 0x35), bytecodeHash)
56+
mstore(add(result, 0x01), shl(96, deployer))
57+
mstore(add(result, 0x15), salt)
58+
result := keccak256(result, 0x55)
59+
}
60+
}
61+
}
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
// Modified from https://github.com/erc6551/reference/tree/main/src/lib
5+
6+
library ERC7656ServiceLib {
7+
function implementation(address service) internal view returns (address implementation_) {
8+
// solhint-disable-next-line no-inline-assembly
9+
assembly {
10+
// copy proxy implementation (0x14 bytes)
11+
extcodecopy(service, 0xC, 0xA, 0x14)
12+
implementation_ := mload(0x00)
13+
}
14+
}
15+
16+
function implementation() internal view returns (address _implementation) {
17+
return implementation(address(this));
18+
}
19+
20+
function linkedData(address service) internal view returns (uint256, bytes12, address, uint256) {
21+
bytes memory encodedData = new bytes(0x60);
22+
// solhint-disable-next-line no-inline-assembly
23+
assembly {
24+
// Copy 0x60 bytes from end of context
25+
extcodecopy(service, add(encodedData, 0x20), 0x4d, 0x60)
26+
}
27+
28+
uint256 chainId;
29+
bytes32 linkedContract;
30+
uint256 linkedId;
31+
32+
// solhint-disable-next-line no-inline-assembly
33+
assembly {
34+
chainId := mload(add(encodedData, 0x20))
35+
linkedContract := mload(add(encodedData, 0x40))
36+
linkedId := mload(add(encodedData, 0x60))
37+
}
38+
39+
bytes12 mode = bytes12(linkedContract);
40+
41+
address extractedAddress = address(uint160(uint256(linkedContract)));
42+
return (chainId, mode, extractedAddress, linkedId);
43+
}
44+
45+
function salt(address service) internal view returns (bytes32) {
46+
bytes memory encodedData = new bytes(0x20);
47+
// solhint-disable-next-line no-inline-assembly
48+
assembly {
49+
// copy 0x20 bytes from beginning of context
50+
extcodecopy(service, add(encodedData, 0x20), 0x2d, 0x20)
51+
}
52+
53+
return abi.decode(encodedData, (bytes32));
54+
}
55+
56+
function salt() internal view returns (bytes32) {
57+
return salt(address(this));
58+
}
59+
60+
}

‎assets/erc-7656/lib/IERC156.sol

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// SPDX-License-Identifier: GPL3
2+
pragma solidity ^0.8.20;
3+
4+
interface IERC165 {
5+
function supportsInterface(bytes4 interfaceId) external view returns (bool);
6+
}

0 commit comments

Comments
 (0)
Please sign in to comment.