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, // 注意这里计算签名使用sendernonce: 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,      // _tokenAddresssafeAddress,      // _safeAddressamount,           // _amountto,              // _beneficiarydeadline,        // _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 Gweiconst 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的情况,不知道是不是测试链的问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/893706.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

离散数学学习笔记

离散数学学习笔记图关系计数基本结构命题

记录一下用烧录夹给M710Q刷魔改bios

买了一台nec7想尝试一下linux桌面系统,机器本来仅仅支持六到七代的CPU,一开始选择的是G4600,但在Ubuntu24.04 桌面环境下有些卡顿,浏览器开的比较慢,于是萌生了上八代U的想法 于是买了CH341A编程器和烧录夹,直接开干 首先一定要说的是,烧录夹真的很不好用,夹成功的完全…

win11家庭版安装wsl2

开启功能 新建一个xxx.bat,右键编辑,将下面这段代码粘贴进去,保存回到桌面,然后右键以管理员身份运行。 然后需要重启。 pushd "%~dp0" dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mum >hyper-v.txt for /f %%i in (findstr /i . hyper-v.txt 2^>n…

1。11

@所有人 网络安全C10-2025.1.11作业:对bluecms进行代码审计,分析复现文件上传、ssti模板注入、文件删除等漏洞2、复习ThinkPHP框架基础知识,重点掌握url传参方式以及获取用户参数的写法3、复习ThinkPHP框架漏洞,重点掌握框架代码执行漏洞及工具使用,了解sql注入漏洞及常见…

1。18

@所有人 网络安全C10-2025.1.18作业:安装jdk并配置多版本jdk切换脚本。安装并破解最新版IDEA,并汉化使用IDEA创建java项目,输出helloworld并打包使用IDEA进行调试,熟练掌握调试方法5、复习java se基础,重点掌握面向对象编程。6、申请deepseek key,安装chatbox、continue、…

Day03_vhdl学习的完善

VHDL代码功能: 针对七段数码管的译码器,将输入的bcd码转换为适合共阴极七段数码管的并行输出代码功能: 不同的结构体使同一实体实现不同功能 注:下次采用Typora自带代码功能插入代码 补充注释,增加注释量 完成设计作业,具有一定创新性和难度英语单词注:今天为第一次练习…

几只毛毛虫?

几只毛毛虫? 题目描述 一天,在生物课上,老师带着小羊和他的同学去公园观察动物。 他看到了草丛里有很多毛毛虫,于是他想,毛毛虫有什么特征呢? 于是他把一条毛毛虫抽象成了一棵有 $n$ 个节点的树。树是一个有 $n$ 个点 $n−1$ 条无向边组成的连通图。 这棵树被称为一条毛毛…

C基础1

基础回顾 Hello World到底是什么🤔 //预处理 #include <stdio.h> //include是找的意思,找到stdio这个头文件//.h是头文件的后缀,.c是c语言源文件的后缀,.cpp是c++源文件的后缀//std是standard标准,i是input输入,o是output输出 //程序的主入口 int main( ) //int表示…

我的Redission使用初体验

本文记录作者第一次使用Redission实现分布式锁的体验。对于碰到的问题进行了一些总结。当我们实际使用redis手写实现分布式锁时,会产生不可重入、不可重试、超时释放和主从一致的问题,此时redission为我们提供的锁完美达成以上要求,适合我们生产时使用。Redisson的实际使用 …

Power Automate 格式化JSON时字段为空

前言最近,在使用Power Automate处理JSON的时候,碰到一个错误。正文1.处理JSON的过程,一般是这样的,如下图:2.测试一下,这样,我们就很容易操作JSON字符串中的属性了,如下图:3.但是,大家在开发中,碰到过这样的问题没有?4.详细的错误信息,如下图:[{"message&qu…

ChromaDB

ChromaDB是一个开源的嵌入式向量数据库,专为存储和查询高维向量数据而设计,特别适用于与大型语言模型(LLMs)和嵌入模型(Embeddings)相关的应用场景 安装pip install chromadb启动服务chroma run --path ./chroma_data --host 0.0.0.0 --port 8000说明: --path ./chroma_…

如何为 Power Automate 配置 Azure Key Vault 权限

前言最近,在Power Automate中使用Azure Key Vault,然后,就需要配置一下AKV的权限。正文1.我们在Azure Portal里新建一个Key vault,如下图:2.进入Access policies,点击Create,如下图:3.如果只是为Power Automate使用,只需要勾选红框的权限就好,如下图:4.搜索需要的服…