ETH 之 合约账户

关于合约账户,我有两个疑问:合约账户的创建,只能发生在合约部署时?合约部署,这是一个交易,只能通过一个EOA账户发起?好,先把合约账户创建聊清楚,上面两个问题也就迎刃而解了合约账户创建情况一:普通创建(CREATE指令)由EOA或合约发起的常规部署:newMyContr

关于合约账户,我有两个疑问:

  1. 合约账户的创建,只能发生在合约部署时?
  2. 合约部署,这是一个交易,只能通过一个EOA账户发起?

好,先把合约账户创建聊清楚,上面两个问题也就迎刃而解了

合约账户创建

情况一:普通创建(CREATE 指令)

EOA 或合约 发起的常规部署:

new MyContract(); // Solidity 中部署新合约

🧮 合约地址计算公式:

address = keccak256(rlp_encode([sender_address, sender_nonce]))[12:]
  • sender_address: 发起部署的账户(EOA 或合约)
  • sender_nonce: 当前账户的 nonce(多少个交易或创建合约)
  • keccak256(...)[12:]: 哈希结果的后 20 字节就是合约地址

💡 举个例子:

假设账户地址是 0xabc...1234,当前 nonce 是 3,那么合约地址就是:

import { keccak256, RLP, getAddress } from "ethers";

const sender = "0xabc...1234";
const nonce = 3;

// RLP 编码
const rlpEncoded = RLP.encode([sender, nonce]);

// keccak256 哈希并截取后 20 字节
const contractAddress = "0x" + keccak256(rlpEncoded).slice(-40);

情况二:确定性创建(CREATE2 指令)

通过 CREATE2 指令(通常用于升级代理、账户抽象、工厂模式等):

new MyContract{salt: bytes32(...)}();

🧮 合约地址计算公式(EIP-1014):

address = keccak256(0xFF ++ deployer_address ++ salt ++ keccak256(bytecode))[12:]
  • deployer_address: 发起部署的地址
  • salt: 任意指定的 32 字节
  • keccak256(bytecode): 合约初始化代码的哈希
  • 0xFF: 固定前缀,避免冲突

💡 重点:CREATE2 允许“预知地址”,甚至还没部署合约前就知道它会在哪里。


🔍 举几个常见的场景

场景 使用方式 地址生成方式
用户钱包部署一个合约 wallet.sendTransaction({data: bytecode}) CREATE(EOA 发起)
工厂合约批量部署子合约 new Child() in Solidity CREATE(合约发起)
升级代理 + CREATE2 部署 CREATE2 工厂 + salt 可预测地址
ERC-4337 账户抽象钱包部署 EntryPoint + CREATE2 可预测地址(用户注册前就生成)

两个问题

问题 1:合约账户的创建,只能发生在合约部署时,对吗?

✔️ 是的,完全正确。

合约账户的创建本质上就是把代码写入链上,并赋予该地址执行能力。

🎯 合约账户的创建只能通过部署合约发生,方式有两个:
  • 使用 CREATE:常规部署
  • 使用 CREATE2:确定性部署

无论哪种方式,都是部署合约的行为,都会创建一个新的合约账户

合约账户不能手动创建

不同于 EOA(只需要私钥就能拥有地址),合约账户必须是:

  • 链上交易执行结果
  • 创建时存入代码和初始化 storage

问题 2:合约部署(创建合约的交易)只能由 EOA 发起,对吗?

✔️ 不完全正确,精确说法:

  • 部署合约的交易只能由 EOA 发起。
  • 但是合约也可以创建其他合约,但不是通过“交易”,而是通过内部调用

合约账户归属

最后聊一个话题:合约账户属于合约的部署者吗?

答案是:不属于。它只是由部署者创建,但不归属于部署者

合约部署后,它就成为了链上的一个独立账户,谁都可以跟它交互,部署者也不能“控制”它,除非在合约代码中显式写入权限控制逻辑

那么如何判断合约的部署者呢?

通常在部署时,把部署者的地址写入合约的状态变量

contract MyToken {
    address public owner;

    constructor() {
        owner = msg.sender; // msg.sender 就是部署者(EOA)
    }

    function mint(address to, uint amount) public {
        require(msg.sender == owner, "Not owner");
        // 进行 mint 逻辑
    }
}
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Henry Wei
Henry Wei
Web3 探索者