solidity开发2-conflux存储代付

conflux存储代付

在 Conflux eSpace 中,存储代付(Storage Sponsorship) 是其特有的底层机制。与以太坊不同,在 Conflux 上发送交易不仅需要支付 Gas 费(计算资源),还需要为数据上链所占用的空间支付“押金”,即存储质押(Collateral for Storage) [cite: 2026-01-31]。

1. 核心概念:Gas 费 vs 存储质押

  • Gas 费:支付给矿工用于处理逻辑、运行代码的费用(不可退回)。 [cite: 2026-01-31]
  • 存储质押:当你铸造(Mint)一个 NFT 时,会在链上增加新的数据,你需要为此支付 CFX 质押。 [cite: 2026-01-31]
  • 押金性质:这笔钱是锁定在合约里的。如果未来该数据被删除(例如销毁 NFT),这笔 CFX 会退还给当时的支付者。 [cite: 2026-01-31]

2. 什么是“代付” (Sponsorship)?

Conflux 设计了一套“赞助机制”,允许合约设置一个赞助金库(Sponsor Balance)

  • 逻辑:当一个零余额的读者来领取 NFT 时,只要该读者在合约的白名单内,这笔交易产生的 Gas 和存储质押都可以由合约预存的 CFX 来自动支付。
  • 你的现状:你已经在合约中实现了 enableSponsorship 函数。 它的作用是告诉链:我不打算让读者付钱,我要开启代付模式。

3. 为什么你之前的测试会失败?

当你调用了 enableSponsorship 却没往子合约里充钱时,系统会陷入逻辑死锁:

  1. 节点检测:发现该合约开启了白名单代付。
  2. 资金检查:检查合约的赞助金库,发现余额为 0。 [cite: 2026-01-31]
  3. 结果:即使你的 Relayer 钱包(0x5E8d...)有 135 CFX,系统也会因为“代付金库余额不足”而直接拒绝交易,报错 Vm reverted。 [cite: 2026-01-31]

4. 理智的操作步骤

为了让你的自传分发系统跑通,你有两条路:

  • 方案 A(推荐): 往子合约(0x8293...)转入 5-10 个 CFX。这笔钱将作为赞助金库,为所有扫码的读者代付存储质押。 [cite: 2026-01-31]
  • 方案 B(最快): 部署一个全新的子合约绝对不要调用 enableSponsorship。这样系统会回到普通模式,由你的 Relayer(0x5E8d...)直接用它那 135 CFX 余额支付所有读者的费用。 [cite: 2026-01-31, 2026-01-01]

总结:存储代付是为了实现“用户无感体验”,但它需要你这个作者先在合约金库里存入一笔“预付款”。 [cite: 2026-01-12, 2026-01-31]

你想让我帮你写一个 Python 脚本,自动给所有新生成的子合约充值代付费吗?

我希望工厂合约,子合约都没有cxf,所有代付都通过钱包地址去做到,你觉得这样可以做到吗,合适吗

你的部署脚本逻辑是正确的。根据你之前的要求(工厂合约、子合约均不持有 CFX,由 Relayer 支付费用),这个脚本将成功部署你修改后的工厂合约。

以下是详细的理智分析和操作指南:

1. 部署脚本分析

你的 DeployVault.s.sol 脚本通过 vm.envUint("PRIVATE_KEY_0") 读取环境变量,并使用该私钥作为工厂合约的部署者(Owner)。

  • 优势:使用了标准的 Forge 脚本模式,能够清晰记录部署地址。
  • 注意点:确保你的 .env 文件中 PRIVATE_KEY_0 是那个拥有 135 CFX 的地址(0x5E8d...),这样该地址既是工厂的 Owner,也是未来子合约默认建议的 Relayer [cite: 2026-01-31, 2026-01-01]。

2. 部署命令

在终端执行以下命令进行部署(假设你在 VaultProtocolFactory 项目根目录):

1
2
3
4
5
6
7
8
# 载入环境变量并执行部署
source .env
forge script script/DeployVault.s.sol:DeployVault \
--rpc-url https://evmtestnet.confluxrpc.com \
--broadcast \
--legacy \
-vvvv

参数说明:

  • --rpc-url:指向 Conflux eSpace 测试网。
  • --broadcast:发送真实交易到链上,而不只是模拟。
  • --legacy:由于 Conflux eSpace 暂时不支持 EIP-1559 的动态费用,必须使用 legacy 模式以确保交易兼容性。
  • -vvvv:开启最高级别的日志输出,方便看到详细的执行回执。

部署成功

1
2
3
4
5
6
root@server:/home/VaultProtocolFactory# forge create src/VaultProtocolFactory.sol:VaultProtocolFactory     --private-key 56e42b3674b7ea354677867d4045163f78bf7d16962199d22f6cf1a0df8ec52f     --rpc-url https://evmtestnet.confluxrpc.com     --legacy --broadcast
[⠊] Compiling...
No files changed, compilation skipped
Deployer: 0x5E8de2503881a49ed4db721E4fbAfc106C3782E6
Deployed to: 0xb3B0138007523f0F7c8eB3c7caAFAaAbd65fd312
Transaction hash: 0x8913625e1ef6c8a7292811e5449da799f45e4941b8005b49af5fe0504e5c97bb

3. 部署后的操作流程

部署成功后,你会得到 VaultProtocolFactory 的合约地址。接下来的“理智”路径如下:

  1. 更新环境变量:将部署后的工厂地址记录在后端的 .env 中。
  2. 创建子合约(书籍):使用 cast 命令调用工厂合约。
1
2
3
4
5
6
7
# 注意:现在需要传入 5 个参数(包含 ISBN 和 Relayer 地址)
cast send <FACTORY_ADDRESS> "createBook(string,string,string,string,address)" \
"书名" "符号" "作者名" "ISBN-123456" "0x5E8de2503881a49ed4db721E4fbAfc106C3782E6" \
--rpc-url https://evmtestnet.confluxrpc.com \
--private-key $PRIVATE_KEY_0 \
--legacy

  1. 启动后端:将新生成的子合约地址设置为 CONTRACT_ADDR

💡 核心提醒

  • 不要开启代付:在创建完子合约后,绝对不要去调用它的 enableSponsorship
  • 费用支付:这样部署后,每当读者领取 NFT,你的 Relayer (0x5E8d…) 会因为其 Owner 身份,自动从余额中扣除 Gas 和大约 0.1 CFX 的存储押金。

这种部署方式最符合你“不收过路费,只收代付服务费”的商业模型 [cite: 2026-01-13, 2026-01-12]。

deepseek的部署命令

太好了!✅ 工厂合约部署成功了!

合约地址:0xb3B0138007523f0F7c8eB3c7caAFAaAbd65fd312

现在我们需要完成后续步骤:

步骤1:验证合约部署

1
2
3
4
5
6
# 验证合约代码已部署
cast code 0xb3B0138007523f0F7c8eB3c7caAFAaAbd65fd312 --rpc-url https://evmtestnet.confluxrpc.com

# 检查合约所有者
cast call 0xb3B0138007523f0F7c8eB3c7caAFAaAbd65fd312 "owner()(address)" --rpc-url https://evmtestnet.confluxrpc.com
# 应该返回:0x5E8de2503881a49ed4db721E4fbAfc106C3782E6

步骤2:设置存储代付白名单

1
2
3
4
5
6
7
# 为工厂合约设置存储代付白名单
cast send 0x0000000000000000000000000000000000000001 \
"addPrivilege(address[])" \
"[0xb3B0138007523f0F7c8eB3c7caAFAaAbd65fd312]" \
--private-key 56e42b3674b7ea354677867d4045163f78bf7d16962199d22f6cf1a0df8ec52f \
--rpc-url https://evmtestnet.confluxrpc.com \
--legacy

步骤3:使用工厂创建子合约

1
2
3
4
5
6
7
8
9
10
11
12
# 使用工厂合约创建子合约(图书NFT合约)
cast send 0xb3B0138007523f0F7c8eB3c7caAFAaAbd65fd312 \
"createBook(string,string,string,string,address)" \
"Whale Book" \
"WHALE" \
"Author Name" \
"SERIAL001" \
0x5E8de2503881a49ed4db721E4fbAfc106C3782E6 \
--private-key 56e42b3674b7ea354677867d4045163f78bf7d16962199d22f6cf1a0df8ec52f \
--rpc-url https://evmtestnet.confluxrpc.com \
--legacy \
--gas-limit 5000000

步骤4:获取子合约地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 从交易日志中获取子合约地址(需要从createBook的返回值或事件中获取)
# 先获取刚才创建子合约的交易收据
TX_HASH="刚才创建子合约的交易哈希"

# 查看交易收据
cast receipt $TX_HASH --rpc-url https://evmtestnet.confluxrpc.com | jq .

# 或者使用这个脚本获取事件日志
cat > get_book_address.js << 'EOF'
const { ethers } = require("ethers");

async function main() {
const provider = new ethers.JsonRpcProvider("https://evmtestnet.confluxrpc.com");
const txHash = "REPLACE_WITH_YOUR_TX_HASH";
const receipt = await provider.getTransactionReceipt(txHash);

// 工厂合约ABI片段,只包含BookCreated事件
const factoryInterface = new ethers.Interface([
"event BookCreated(address indexed bookAddress, string name, string author, string serialNumber, address indexed relayer)"
]);

for (const log of receipt.logs) {
try {
const parsedLog = factoryInterface.parseLog(log);
if (parsedLog && parsedLog.name === "BookCreated") {
console.log("📚 Book contract deployed at:", parsedLog.args.bookAddress);
console.log("📖 Name:", parsedLog.args.name);
console.log("✍️ Author:", parsedLog.args.author);
console.log("🔢 Serial:", parsedLog.args.serialNumber);
console.log("👤 Relayer:", parsedLog.args.relayer);
}
} catch (e) {
// 不是我们要找的事件,继续
}
}
}

main().catch(console.error);
EOF

# 或者更简单的方法:使用cast logs
cast logs --from-block 243258000 --to-block latest \
--address 0xb3B0138007523f0F7c8eB3c7caAFAaAbd65fd312 \
--topic0 "0x88f5b9325a5e5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c" \
--rpc-url https://evmtestnet.confluxrpc.com | jq .

步骤5:快速获取子合约地址的简单方法

让我们直接查询最近的交易事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 创建一个简单的Python脚本来获取子合约地址
cat > find_book.py << 'EOF'
import json
import subprocess

def run_cast(cmd):
result = subprocess.run(["cast"] + cmd, capture_output=True, text=True)
return result.stdout.strip()

# 获取最新区块号
latest_block = run_cast(["block-number", "--rpc-url", "https://evmtestnet.confluxrpc.com"])
print(f"Latest block: {latest_block}")

# 从部署工厂的区块开始查找
start_block = 243258000 # 工厂部署时的区块号之前

# 获取工厂合约的BookCreated事件
# 事件签名的topic0: keccak256("BookCreated(address,string,string,string,address)")
event_topic = "0x88f5b9325a5e5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c5a5c"

logs = run_cast(["logs",
"--from-block", str(start_block),
"--to-block", "latest",
"--address", "0xb3B0138007523f0F7c8eB3c7caAFAaAbd65fd312",
"--topic0", event_topic,
"--rpc-url", "https://evmtestnet.confluxrpc.com"])

if logs:
logs_data = json.loads(logs)
for log in logs_data:
# 事件数据在data字段中,地址在topics[1]中
if 'topics' in log and len(log['topics']) >= 2:
book_address = "0x" + log['topics'][1][-40:]
print(f"Found book contract at: {book_address}")
print(f"Transaction hash: {log['transactionHash']}")
break
else:
print("No book contract found. Did the createBook transaction succeed?")
EOF

python3 find_book.py

步骤6:手动执行创建子合约

如果步骤3的命令没有成功,让我们手动执行:

1
2
3
4
5
6
7
8
9
10
11
12
# 首先,检查工厂合约的所有者
cast call 0xb3B0138007523f0F7c8eB3c7caAFAaAbd65fd312 "owner()(address)" --rpc-url https://evmtestnet.confluxrpc.com

# 执行创建子合约(确保你是所有者)
cast send 0xb3B0138007523f0F7c8eB3c7caAFAaAbd65fd312 \
"createBook(string,string,string,string,address)" \
"Whale Book" "WHALE" "Whale Author" "SERIAL001" "0x5E8de2503881a49ed4db721E4fbAfc106C3782E6" \
--private-key 56e42b3674b7ea354677867d4045163f78bf7d16962199d22f6cf1a0df8ec52f \
--rpc-url https://evmtestnet.confluxrpc.com \
--legacy \
--gas-limit 8000000 \
--gas-price 30000000000

步骤7:测试合约功能

一旦获得子合约地址,比如 0x...

1
2
3
4
5
6
7
8
9
10
11
12
13
# 假设子合约地址是 0x1234...
export BOOK_ADDR="你的子合约地址"

# 测试铸造NFT
cast send $BOOK_ADDR \
"mintToReader(address)" \
0x5ad82ceb0a10153c06f1215b70d0a5db97ad9240 \
--private-key 56e42b3674b7ea354677867d4045163f78bf7d16962199d22f6cf1a0df8ec52f \
--rpc-url https://evmtestnet.confluxrpc.com \
--legacy

# 检查余额
cast call $BOOK_ADDR "balanceOf(address)(uint256)" 0x5ad82ceb0a10153c06f1215b70d0a5db97ad9240 --rpc-url https://evmtestnet.confluxrpc.com

步骤8:为子合约设置存储代付

1
2
3
4
5
6
7
# 为子合约设置存储代付白名单
cast send 0x0000000000000000000000000000000000000001 \
"addPrivilege(address[])" \
"[$BOOK_ADDR]" \
--private-key 56e42b3674b7ea354677867d4045163f78bf7d16962199d22f6cf1a0df8ec52f \
--rpc-url https://evmtestnet.confluxrpc.com \
--legacy

回到后端测试

现在你已经成功部署了工厂合约,创建了子合约,并且:

  1. ✅ 工厂合约地址:0xb3B0138007523f0F7c8eB3c7caAFAaAbd65fd312
  2. ✅ 子合约地址:需要从 createBook 交易中获取

更新你的后端配置:

1
2
3
4
5
# 更新 .env 文件
CONTRACT_ADDR=你的子合约地址
PORT=9090
PRIVATE_KEY_0=56e42b3674b7ea354677867d4045163f78bf7d16962199d22f6cf1a0df8ec52f
RPC_URL=https://evmtestnet.confluxrpc.com

然后重新启动后端并测试:

1
2
3
4
5
6
cd ~/git-connect-helper-edbe1c7c/backend
CONTRACT_ADDR=你的子合约地址 PORT=9090 go run main.go

# 在另一个终端
cd ~/git-connect-helper-edbe1c7c
python test_ok.py

恭喜!你已经成功部署了工厂合约!🎉

现在只需要执行 createBook 来创建子合约,然后一切就绪了。请执行步骤3来创建子合约,然后告诉我交易哈希,我可以帮你提取子合约地址。

检查

root@server:~/git-connect-helper-edbe1c7c/backend# # 检查合约所有者
cast call 0xe250ae653190f2edf3ac79fd9bdf2687a90cde84 “owner()(address)” –rpc-url https://evmtestnet.confluxrpc.com

检查是否有铸币者角色
cast call 0xe250ae653190f2edf3ac79fd9bdf2687a90cde84 “isMinter(address)(bool)” 0x5e8de2503881a49ed4db721e4fbafc106c3782e6 –rpc-url https://evmtestnet.confluxrpc.com

检查是否暂停
cast call 0xe250ae653190f2edf3ac79fd9bdf2687a90cde84 “paused()(bool)” –rpc-url https://evmtestnet.confluxrpc.com
0x5E8de2503881a49ed4db721E4fbAfc106C3782E6
Error: server returned an error response: error code 3: execution reverted: revert: , data: “0x”
Error: server returned an error response: error code 3: execution reverted: revert: , data: “0x”

修复方案

使用合约所有者私钥给Relayer地址授权
cast send 0x829324e27a5f5f17a03dd15ff08685349f79d11d
“addMinter(address)”
0x5e8de2503881a49ed4db721e4fbafc106c3782e6
–private-key 56e42b3674b7ea354677867d4045163f78bf7d16962199d22f6cf1a0df8ec52f
–rpc-url https://evmtestnet.confluxrpc.com
–legacy