aere-contracts/contracts/tokenbound/ERC6551Registry.sol
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:02:37 +03:00

173 lines
6.3 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
// Canonical ERC-6551 Registry — verbatim reference implementation from
// https://github.com/erc6551/reference (src/ERC6551Registry.sol). This is a
// FIXED REFERENCE CONTRACT, not a token: it deploys deterministic ERC-1167
// proxy accounts (via CREATE2) bound to (chainId, tokenContract, tokenId) and
// holds no state, mints nothing, and has no owner/admin. The public canonical
// deployment lives at 0x000000006551c19487814612e58FE06813775758 on chains
// that received the deterministic Nick-factory deploy; on AERE chain 2800 we
// deploy the identical source (the address differs because the deterministic
// factory salt is not replayed here — the bytecode/behaviour are identical).
interface IERC6551Registry {
/**
* @dev The registry MUST emit the ERC6551AccountCreated event upon successful account creation.
*/
event ERC6551AccountCreated(
address account,
address indexed implementation,
bytes32 salt,
uint256 chainId,
address indexed tokenContract,
uint256 indexed tokenId
);
/**
* @dev The registry MUST revert with AccountCreationFailed error if the create2 operation fails.
*/
error AccountCreationFailed();
/**
* @dev Creates a token bound account for a non-fungible token.
*
* If account has already been created, returns the account address without calling create2.
*
* Emits ERC6551AccountCreated event.
*
* @return account The address of the token bound account
*/
function createAccount(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external returns (address account);
/**
* @dev Returns the computed token bound account address for a non-fungible token.
*
* @return account The address of the token bound account
*/
function account(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external view returns (address account);
}
contract ERC6551Registry is IERC6551Registry {
function createAccount(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external returns (address) {
assembly {
// Memory Layout:
// ----
// 0x00 0xff (1 byte)
// 0x01 registry (address) (20 bytes)
// 0x15 salt (bytes32) (32 bytes)
// 0x35 Bytecode Hash (bytes32) (32 bytes)
// ----
// 0x55 ERC-1167 Constructor + Header (20 bytes)
// 0x69 implementation (address) (20 bytes)
// 0x5D ERC-1167 Footer (15 bytes)
// 0x8C salt (uint256) (32 bytes)
// 0xAC chainId (uint256) (32 bytes)
// 0xCC tokenContract (address) (32 bytes)
// 0xEC tokenId (uint256) (32 bytes)
// Silence unused variable warnings
pop(chainId)
// Copy bytecode + constant data to memory
calldatacopy(0x8c, 0x24, 0x80) // salt, chainId, tokenContract, tokenId
mstore(0x6c, 0x5af43d82803e903d91602b57fd5bf3) // ERC-1167 footer
mstore(0x5d, implementation) // implementation
mstore(0x49, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // ERC-1167 constructor + header
// Copy create2 computation data to memory
mstore8(0x00, 0xff) // 0xFF
mstore(0x35, keccak256(0x55, 0xb7)) // keccak256(bytecode)
mstore(0x01, shl(96, address())) // registry address
mstore(0x15, salt) // salt
// Compute account address
let computed := keccak256(0x00, 0x55)
// If the account has not yet been deployed
if iszero(extcodesize(computed)) {
// Deploy account contract
let deployed := create2(0, 0x55, 0xb7, salt)
// Revert if the deployment fails
if iszero(deployed) {
mstore(0x00, 0x20188a59) // `AccountCreationFailed()`
revert(0x1c, 0x04)
}
// Store account address in memory before salt and chainId
mstore(0x6c, deployed)
// Emit the ERC6551AccountCreated event
log4(
0x6c,
0x60,
// `ERC6551AccountCreated(address,address,bytes32,uint256,address,uint256)`
0x79f19b3655ee38b1ce526556b7731a20c8f218fbda4a3990b6cc4172fdf88722,
implementation,
tokenContract,
tokenId
)
// Return the account address
return(0x6c, 0x20)
}
// Otherwise, return the computed account address
mstore(0x00, shr(96, shl(96, computed)))
return(0x00, 0x20)
}
}
function account(
address implementation,
bytes32 salt,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external view returns (address) {
assembly {
// Silence unused variable warnings
pop(chainId)
pop(tokenContract)
pop(tokenId)
// Copy bytecode + constant data to memory
calldatacopy(0x8c, 0x24, 0x80) // salt, chainId, tokenContract, tokenId
mstore(0x6c, 0x5af43d82803e903d91602b57fd5bf3) // ERC-1167 footer
mstore(0x5d, implementation) // implementation
mstore(0x49, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // ERC-1167 constructor + header
// Copy create2 computation data to memory
mstore8(0x00, 0xff) // 0xFF
mstore(0x35, keccak256(0x55, 0xb7)) // keccak256(bytecode)
mstore(0x01, shl(96, address())) // registry address
mstore(0x15, salt) // salt
// Store computed account address in memory
mstore(0x00, shr(96, shl(96, keccak256(0x00, 0x55))))
// Return computed account address
return(0x00, 0x20)
}
}
}