如何为Safe模块AllowanceModule的executeAllowanceTransfer生成有效签名?
解决executeAllowanceTransfer签名错误的方案
针对你遇到的错误"expectedDelegate == signer && delegates[address(safe)][uint48(signer)].delegate == signer",核心问题出在委托关系不匹配或签名构造不符合EIP-712规范,以下是具体解决步骤:
1. 修复委托关系
该错误的直接原因是签名账户(signer)在对应Safe下的委托设置异常:合约要求签名者必须是自己的委托代理(即delegates[safe][signer].delegate == signer)。
- 如果你之前给该signer设置过委托(调用过
setDelegate将其委托给其他账户),需要先调用revokeDelegate(safeAddress, signerAddress)取消委托,或者调用setDelegate(safeAddress, signerAddress, signerAddress)将委托设回自己。 - 可以通过调用合约的
delegates(safeAddress, signerAddress)方法查询当前委托状态,确认返回值等于signer地址。
2. 使用EIP-712结构化数据签名
executeAllowanceTransfer要求的是EIP-712签名,而非普通的signMessage(普通签名会添加以太坊消息前缀,与合约预期的哈希不匹配)。正确的签名构造步骤如下(以ethers.js为例):
// 1. 定义EIP-712类型(需与AllowanceModule合约内的结构体一致) const types = { AllowanceTransfer: [ { name: "safe", type: "address" }, { name: "token", type: "address" }, { name: "to", type: "address" }, { name: "value", type: "uint256" }, { name: "payment", type: "uint256" }, { name: "nonce", type: "uint48" }, { name: "expiration", type: "uint48" } ] }; // 2. 构造域名分隔符数据(需匹配合约内的EIP712配置) const domain = { name: "AllowanceModule", // 合约中定义的EIP712_NAME version: "1", // 合约中定义的EIP712_VERSION chainId: await ethers.provider.getNetwork().chainId, verifyingContract: "你的AllowanceModule合约地址" }; // 3. 构造转账参数数据 const transferParams = { safe: "你的Safe合约地址", token: "转账代币地址", to: "接收方地址", value: ethers.utils.parseUnits("转账金额", 18), // 需根据代币小数位调整 payment: ethers.utils.parseUnits("手续费金额", 18), // 无手续费填0 nonce: await allowanceModuleContract.nonces("你的Safe合约地址", "签名者地址"), // 必须获取当前最新nonce expiration: Math.floor(Date.now() / 1000) + 3600 // 过期时间,比如1小时后 }; // 4. 生成EIP-712签名 const signature = await signer._signTypedData(domain, types, transferParams);
3. 验证关键参数正确性
- Nonce必须准确:每次executeAllowanceTransfer后,对应signer在该Safe下的nonce会递增,必须使用合约返回的最新值,否则签名会被判定无效。
- 过期时间有效:expiration必须大于当前区块时间,否则签名直接失效。
内容的提问来源于stack exchange,提问作者nas
相关产品推荐
相关产品推荐

