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号
浙公网安备 33010602011771号