safe合约调用手动形式和sdk形式比较
目的是发起ERC20合约转账,
以下是手动调用合约形式:
// tokenTransfer3:可以运行,手动写代码的方式
async tokenTransfer3(
safeAddress: string,
tokenAddress: string,
to: string,
amount: string,
privateKeys: string[], // Safe 所有者的私钥列表
): Promise<TransactionResult> {
if (!this.safeSdk) {
await this.initSafe(safeAddress);
}
if (!this.safeSdk) {
throw new Error('Safe SDK 初始化失败');
}
const provider = new ethers.JsonRpcProvider(safeConfig.RPC_URL);
const deadline = Math.floor(Date.now() / 1000) + 3600; // 1小时后过期
// 创建领取代币模块合约实例
const tokenWithdrawModule = new ethers.Contract(
safeConfig.TOKEN_WITHDRAW_MODULE_ADDRESS,
TokenWithdrawModuleABI,
provider
);
const { deployer } = await getOwner();
// 获取当前nonce - 注意这里需要传入三个参数
const currentNonce = await tokenWithdrawModule.nonces(deployer.address);
// 准备EIP-712签名数据
const domain = {
name: 'TokenWithdrawModuleV2', // 注意这里合约名称改了
version: '1',
chainId: safeConfig.CHAIN_ID,
verifyingContract: safeConfig.TOKEN_WITHDRAW_MODULE_ADDRESS
};
const types = {
TokenWithdrawModuleV2: [ // 注意这里类型名称也要改
{ name: 'tokenAddress', type: 'address' }, // 注意这里签名的字段顺序要和合约一致
{ name: 'safeAddress', type: 'address' },
{ name: 'amount', type: 'uint256' },
{ name: '_beneficiary', type: 'address' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' }
]
};
const message = {
tokenAddress, // 注意这里字段顺序要和类型定义一致
safeAddress,
amount,
_beneficiary: deployer.address, // 注意这里计算签名使用sender
nonce: Number(currentNonce),
deadline
};
this.logger.warn(`准备签名数据: amount=${amount}, to=${to}, deadline=${deadline}`);
// 收集所有签名
const signatures: string[] = [];
// deployer签名
const deployerSignature = await deployer.signTypedData(domain, types, message);
signatures.push(deployerSignature);
this.logger.warn(`Deployer已签名: ${deployer.address.slice(0, 10)}...`);
// // 其他签名者签名
// for (const privateKey of privateKeys) {
// const signer = new ethers.Wallet(privateKey, provider);
// const signature = await signer.signTypedData(domain, types, message);
// signatures.push(signature);
// this.logger.warn(`签名者已签名: ${signer.address.slice(0, 10)}...`);
// }
// 合并所有签名
const combinedSignature = ethers.concat(signatures.map(sig => ethers.getBytes(sig)));
// 调用模块合约的tokenTransfer方法
const moduleWithSigner = tokenWithdrawModule.connect(deployer);
const tx = await (moduleWithSigner as any).tokenTransfer(
tokenAddress,
safeAddress,
amount,
to,
deadline,
combinedSignature,
{ gasLimit: 500000 }
);
this.logger.warn(`交易已发送: ${tx.hash}`);
const receipt = await tx.wait();
this.logger.warn(`交易已确认: ${receipt.hash}`);
return receipt;
}
采用sdk调用形式:
async tokenTransfer(
safeAddress: string,
tokenAddress: string,
to: string,
amount: string,
privateKeys: string[],
): Promise<TransactionResult> {
// 如果尚未初始化,则先初始化 Safe 实例
if (!this.safeSdk) {
await this.initSafe(safeAddress);
}
if (!this.safeSdk) {
throw new Error('Safe SDK 初始化失败');
}
const provider = new ethers.JsonRpcProvider(safeConfig.RPC_URL);
const deadline = Math.floor(Date.now() / 1000) + 3600; // 1小时后过期
// 编码合约调用数据
const abiF = new ethers.Interface(TokenWithdrawModuleABI);
const encodeFunctionData = abiF.encodeFunctionData('tokenTransfer', [
tokenAddress, // _tokenAddress
safeAddress, // _safeAddress
amount, // _amount
to, // _beneficiary
deadline, // _deadline
'0x' // _signatures - 空签名,SDK会处理签名
]);
// 构造Safe交易数据
const safeTxData = {
to: safeConfig.TOKEN_WITHDRAW_MODULE_ADDRESS,
value: '0',
data: encodeFunctionData,
operation: 0,
};
// 使用Safe SDK组装交易
const safeTransaction = await this.safeSdk.createTransaction({
transactions: [safeTxData],
options: {
safeTxGas: '500000',
}
});
this.logger.log('交易数据组装成功');
// 使用deployer签名
const { deployer } = await getOwner();
await this.safeSdk.signTransaction(safeTransaction, deployer.address);
this.logger.log(`Deployer已签名: ${deployer.address}`);
// 收集其他签名者的签名
for (const privateKey of privateKeys) {
const signer = new ethers.Wallet(privateKey, provider);
await this.safeSdk.signTransaction(safeTransaction, signer.address);
this.logger.log(`签名者已签名: ${signer.address}`);
}
// 执行交易
const feeData = await provider.getFeeData();
const gasPrice = feeData.gasPrice ? (feeData.gasPrice * 12n / 10n).toString() : ethers.parseUnits('50', 'gwei').toString(); // 使用当前gas价格的1.2倍或默认50 Gwei
const tx = await this.safeSdk.executeTransaction(safeTransaction, {
gasLimit: 500000,
gasPrice
});
this.logger.log(`交易已发送,交易哈希 ${tx.hash},使用的gas价格: ${ethers.formatUnits(gasPrice, 'gwei')} Gwei`);
// 等待交易被确认
const receipt = await provider.waitForTransaction(tx.hash);
if (!receipt) {
throw new Error('交易等待超时');
}
this.logger.log(`交易已确认,区块号: ${receipt.blockNumber}`);
if (receipt.status === 0) {
throw new Error('交易执行失败');
}
return tx;
}
sdk调用形式会出现一直pending的情况,不知道是不是测试链的问题

浙公网安备 33010602011771号