You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Solidity双层mapping存储写入失败求助:Hardhat操作gardens[20][22]遇阻

问题:无法通过Hardhat将地址写入Solidity嵌套mapping的指定位置

我正在学习Solidity存储机制,参与Node Guardian任务,部署了地址为0xb1d01315081351dbb747c3db92754acf8686a219的智能合约,合约代码如下:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

contract Temple {
uint128 public entrance;
address public mainHall;
mapping(uint8 => mapping(uint8 => address)) public gardens;
bytes20[] public chambers;

/// Write data to the contract's ith storage slot
function write(uint256 i, bytes32 data) public {
    assembly {
        sstore(i, data)
    }
 }
}

我需要通过Hardhat将我的地址写入gardens[20][22],编写了以下代码但操作失败:

const { ethers } = require("hardhat");

async function main() {
const [deployer] = await ethers.getSigners();

console.log("Deploying contracts with the account:", deployer.address);

const temple = await ethers.getContractAt("Temple", "0xb1d01315081351DBb747c3DB92754acF8686A219");
// const Temple = await ethers.getContractFactory("Temple");
// const temple = await Temple.deploy();

console.log("Temple address:", temple.address);

// Set a higher gas limit for writing data to the contract
const gasLimit = 300000; // Adjust this value as needed

// Convert your address to bytes32
// const encodedAddress = ethers.utils.defaultAbiCoder.encode(["address"], [deployer.address]);
const encodedAddress = ethers.utils.defaultAbiCoder.encode(["address"], [deployer.address]);

// Calculate the storage slot for gardens[20][2]
const slotForGardens20 = ethers.utils.keccak256(ethers.utils.solidityPack(["uint8", "uint8"], [20, 2]));

// Calculate the storage slot for gardens[20][22]
const slotForGardens2022 = ethers.utils.keccak256(ethers.utils.solidityPack(["uint8", "bytes32"], [22, slotForGardens20]));

// Use the write function to write your address to gardens[20][22]
await temple.write(slotForGardens2022, encodedAddress);
await temple.write(ethers.utils.keccak256(ethers.utils.solidityPack(["uint8", "uint8"], [20, 22])), encodedAddress, { gasLimit });


// const addressInSlot = await temple.read(20, 22);
// console.log("Address read from gardens[20][22]:", addressInSlot);

}

main()
.then(() => process.exit(0))
.catch(error => {
    console.error(error);
    process.exit(1);
});

问题分析与修正方案

核心错误:存储槽计算逻辑错误

Solidity中嵌套mapping的存储槽计算必须遵循固定规则:

  1. 确定外层mapping的基础槽位:合约变量按声明顺序分配槽位,entrance占槽0,mainHall占槽1,因此gardens的基础槽位是2。
  2. 计算外层mapping的索引哈希:keccak256(abi.encodePacked(外层key, 外层mapping的槽位))
  3. 计算内层mapping的最终槽位:keccak256(abi.encodePacked(内层key, 外层索引哈希))

你的代码错误在于未使用gardens的基础槽位,直接用两个key组合计算,导致槽位完全偏离实际位置。

修正后的Hardhat代码

const { ethers } = require("hardhat");

async function main() {
  const [deployer] = await ethers.getSigners();
  console.log("Using account:", deployer.address);

  const temple = await ethers.getContractAt("Temple", "0xb1d01315081351DBb747c3DB92754acF8686A219");
  console.log("Temple contract address:", temple.address);

  // 1. gardens的基础存储槽是2(根据合约变量声明顺序)
  const gardensBaseSlot = 2;

  // 2. 计算外层mapping(gardens[20])的哈希值
  const outerHash = ethers.utils.keccak256(
    ethers.utils.solidityPack(["uint8", "uint256"], [20, gardensBaseSlot])
  );

  // 3. 计算gardens[20][22]的最终存储槽
  const targetSlot = ethers.utils.keccak256(
    ethers.utils.solidityPack(["uint8", "bytes32"], [22, outerHash])
  );

  // 4. 将地址编码为bytes32(地址自动左对齐填充到32字节)
  const encodedAddress = ethers.utils.defaultAbiCoder.encode(["address"], [deployer.address]);

  // 5. 写入目标存储槽
  const tx = await temple.write(targetSlot, encodedAddress, { gasLimit: 300000 });
  await tx.wait();
  console.log("Transaction confirmed, address written to gardens[20][22]");

  // 可选:验证写入结果(无需合约read方法)
  // const storedValue = await ethers.provider.getStorageAt(temple.address, targetSlot);
  // console.log("Stored value (bytes32):", storedValue);
  // console.log("Decoded address:", ethers.utils.defaultAbiCoder.decode(["address"], storedValue)[0]);
}

main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

额外说明

  • 可通过ethers.provider.getStorageAt直接读取目标槽位值并解码,验证写入结果。
  • 确保调用账户拥有足够的Goerli测试网ETH支付gas费用。

内容的提问来源于stack exchange,提问作者ThomasJanko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 14:55:54