EIP1559与传统Gas定价模型转账逻辑

传统的gas定价模型(Txn Type===0)

20230104-112422.jpg
import Web3 from 'web3';
import { AbiItem } from 'web3-utils/types/index.d';
import erc20 from '../abis/erc20.json';
import { FeeMarketEIP1559Transaction as Tx } from '@ethereumjs/tx';
import { Common, Chain, Hardfork } from '@ethereumjs/common';
import config from '../constants/service.config';
const webSocketProvider = (rpcUrl: string) => {
  return new Web3.providers.HttpProvider(rpcUrl);
};
const provider = (rpcUrl: string = config.alchemyEthBaseUrl) => {
  return new Web3(webSocketProvider(rpcUrl));
};
const transfer = async (
  { ...rest }: any
) => {
  const {
    privateKey,
    contractAddress,
    tokenAddress,
    recipientAddress,
    rpcUrl,
    senderAddress,
    amount,
    ...args
  } = rest;
  const web3 = provider();
  const { toHex, toWei, hexToNumber } = web3.utils;
  const gasPrice = await web3.eth.getGasPrice();
  try {
    const nounce = await web3.eth.getTransactionCount(senderAddress, 'pending');
    let rawTx = {};
    if (['eth', 'matic'].includes(tokenAddress.toLowerCase())) {
      // 以太坊主币
      rawTx = {
        to: recipientAddress,
        value: toHex(toWei(String(amount))),
      };
    } else {
      // 符合erc20规范的代币
      const contract = new web3.eth.Contract(erc20 as AbiItem[], tokenAddress, {
        from: senderAddress,
      });
      const transferEvent = contract.methods.transfer(
        recipientAddress,
        toHex(toWei(String(amount)))
      );
      const limit = await transferEvent.estimateGas(); // gas limit
      rawTx = {
        to: tokenAddress,
        gasLimit: toHex(Math.ceil(limit * 1.2)),
        data: transferEvent.encodeABI(),
      };
    }
    /**
     * 组装交易数据:
     * to: 
     *    如果是以太坊主币,to是收款人地址
     *    如果是erc20代码,to是协议地址,收款人地址在合约方法:contrack.methods.transfer中定义
     * value:
     *    如果是以太坊主币,value是转账金额
     *    如果是erc20代码,value是`toHex(0)`,转账金额在合约方法:contrack.methods.transfer中定义
     * maxPriorityFeePerGas
     *    Txn Type=2所必需,提高优先级,给予矿工的小费
     * maxFeePerGas
     *    Txn Type=2所必需,提高优先级,给予矿工小费的最大值
    */
    rawTx = {
      from: senderAddress,
      nonce: toHex(nounce),
      gasLimit: toHex(21000), //"21000", 
      gasPrice: toHex(gasPrice),
      value: '0x00', //"10000000",//'0x00',
      ...rawTx,
    };
    /**
     * 以太坊资源的通用实现类;
     *    创建以太坊链和分叉的实例,实现EIP1559功能需要选择London硬分叉
     *    new Common({ chain: Chain.Mainnet, eips: [1559] })
    */
    const common = new Common({
      chain: Chain.Goerli,
      hardfork: Hardfork.London,
    });
    const unsignedTx = Tx.fromTxData(rawTx, { common });
    const secretKey = Buffer.from(privateKey.slice(2), 'hex');
    const signedTx = unsignedTx.sign(secretKey);
    const serializedTx = signedTx.serialize();
    const signedTransactionData = '0x' + serializedTx.toString('hex');
    web3.eth
      .sendSignedTransaction(signedTransactionData, async function (err, hash) {
        if (!err) {
          /**
           * gasfee = `${web3.utils.fromWei(
            (Number((rawTx as any).gasLimit) * Number(gasPrice)).toString()
          )}${wallet.unit}`;
          */
          console.log('-----hash-------', hash);
          const url = `https://goerli.etherscan.io/tx/${hash}`;
          const accepted = `https://etherscan.io/api?module=localchk&action=txexist&txhash=${hash}`;
          console.log('TX Link', url);
          console.log('Accepted', accepted);
        } else {
          console.log('-----error', err);
        }
      })
      .on('receipt', (receipt) => {
        console.log('TX receipt', receipt);
        //             blockHash: ""
        // blockNumber: 
        // contractAddress: null
        // cumulativeGasUsed: 
        // effectiveGasPrice: 
        // from: ""
        // gasUsed: 21000
        // logs: []
        // logsBloom: ""
        // status: true
        // to: ""
        // transactionHash: ""
        // transactionIndex: 
        // type: "0x0"
        const { gasUsed, status } = receipt;
        /**
         * gasfee = `${web3.utils.fromWei(
          (gasUsed * Number(gasPrice)).toString()
        )}${wallet.unit}`;
        */
      });
  } catch (e) {
    console.log(e);
  }
};

伦敦硬分叉EIP1559(Txn Type===2)

20230104-112437.jpg
import Web3 from 'web3';
import { AbiItem } from 'web3-utils/types/index.d';
import erc20 from '../abis/erc20.json';
import { FeeMarketEIP1559Transaction as Tx } from '@ethereumjs/tx';
import { Common, Chain, Hardfork } from '@ethereumjs/common';
import config from '../constants/service.config';
const webSocketProvider = (rpcUrl: string) => {
  return new Web3.providers.HttpProvider(rpcUrl);
};
const provider = (rpcUrl: string = config.alchemyEthBaseUrl) => {
  return new Web3(webSocketProvider(rpcUrl));
};
const transfer = async (
  { ...rest }: any
) => {
  const {
    privateKey,
    contractAddress,
    tokenAddress,
    recipientAddress,
    rpcUrl,
    senderAddress,
    amount,
    ...args
  } = rest;
  const web3 = provider();
  const { toHex, toWei, hexToNumber } = web3.utils;
  const gasPrice = await web3.eth.getGasPrice();
  try {
    const nounce = await web3.eth.getTransactionCount(senderAddress, 'pending');
    let rawTx = {};
    if (['eth', 'matic'].includes(tokenAddress.toLowerCase())) {
      // 以太坊主币
      rawTx = {
        to: recipientAddress,
        value: toHex(toWei(String(amount))),
      };
    } else {
      // 符合erc20规范的代币
      const contract = new web3.eth.Contract(erc20 as AbiItem[], tokenAddress, {
        from: senderAddress,
      });
      const transferEvent = contract.methods.transfer(
        recipientAddress,
        toHex(toWei(String(amount)))
      );
      const limit = await transferEvent.estimateGas(); // gas limit
      rawTx = {
        to: tokenAddress,
        gasLimit: toHex(Math.ceil(limit * 1.2)),
        data: transferEvent.encodeABI(),
      };
    }
    // maxPriorityFeePerGas: Txn Type === 2所需要的字段
    const maxPriorityFeePerGasRes = request.post(config.alchemyEthBaseUrl, {
      id: 1,
      jsonrpc: '2.0',
      method: 'eth_maxPriorityFeePerGas',
    });
    const maxPriorityFeePerGas = ((await maxPriorityFeePerGasRes) as any)
      .result;
    const getBlockRes = web3.eth.getBlock('latest');
    const baseFeePerGas = ((await getBlockRes) as any).baseFeePerGas - 1;
    /**
     * 组装交易数据:
     * to: 
     *    如果是以太坊主币,to是收款人地址
     *    如果是erc20代码,to是协议地址,收款人地址在合约方法:contrack.methods.transfer中定义
     * value:
     *    如果是以太坊主币,value是转账金额
     *    如果是erc20代码,value是`toHex(0)`,转账金额在合约方法:contrack.methods.transfer中定义
     * maxPriorityFeePerGas
     *    Txn Type=2所必需,提高优先级,给予矿工的小费
     * maxFeePerGas
     *    Txn Type=2所必需,提高优先级,给予矿工小费的最大值
    */
    rawTx = {
      from: senderAddress,
      nonce: toHex(nounce),
      gasLimit: toHex(21000), //"21000",
      maxPriorityFeePerGas: maxPriorityFeePerGas,
      maxFeePerGas: toHex(
        2 * baseFeePerGas + hexToNumber(maxPriorityFeePerGas)
      ),
      value: '0x00', //"10000000",//'0x00',
      ...rawTx,
    };
    /**
     * 以太坊资源的通用实现类;
     *    创建以太坊链和分叉的实例,实现EIP1559功能需要选择London硬分叉
     *    new Common({ chain: Chain.Mainnet, eips: [1559] })
    */
    const common = new Common({
      chain: Chain.Goerli,
      hardfork: Hardfork.London,
    });
    const unsignedTx = Tx.fromTxData(rawTx, { common });
    const secretKey = Buffer.from(privateKey.slice(2), 'hex');
    const signedTx = unsignedTx.sign(secretKey);
    const serializedTx = signedTx.serialize();
    const signedTransactionData = '0x' + serializedTx.toString('hex');
    web3.eth
      .sendSignedTransaction(signedTransactionData, async function (err, hash) {
        if (!err) {
          /**
           * gasfee = `${web3.utils.fromWei(
            (Number((rawTx as any).gasLimit) * Number(gasPrice)).toString()
          )}${wallet.unit}`;
          */
          console.log('-----hash-------', hash);
          const url = `https://goerli.etherscan.io/tx/${hash}`;
          const accepted = `https://etherscan.io/api?module=localchk&action=txexist&txhash=${hash}`;
          console.log('TX Link', url);
          console.log('Accepted', accepted);
        } else {
          console.log('-----error', err);
        }
      })
      .on('receipt', (receipt) => {
        console.log('TX receipt', receipt);
        // blockHash: ""
        // blockNumber: 
        // contractAddress: null
        // cumulativeGasUsed: 
        // effectiveGasPrice: 
        // from: ""
        // gasUsed: 21000
        // logs: []
        // logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
        // status: true
        // to: ""
        // transactionHash: ""
        // transactionIndex: 
        // type: "0x0"
        const { gasUsed, status } = receipt;
        /**
         * gasfee = `${web3.utils.fromWei(
          (gasUsed * Number(gasPrice)).toString()
        )}${wallet.unit}`;
        */
      });
  } catch (e) {
    console.log(e);
  }
};

外部签名

import Web3 from 'web3';
import { bufArrToArr, toBuffer, bigIntToHex, addHexPrefix } from '@ethereumjs/util'
...
// signatures是外部服务签名
const { toHex, toWei, toDecimal } = web3.utils;
const { r_hex: r, s_hex: s, recovery_id_hex } = signatures
const chainId = unsignedTx.common.chainId()
const v = chainId === undefined
  ? BigInt(toDecimal(addHexPrefix(recovery_id_hex)) + 27)
  : BigInt(toDecimal(addHexPrefix(recovery_id_hex)) + 35) + BigInt(chainId) * BigInt(2)
const toSignTx = {
  nonce: unsignedTx.nonce,
  gasPrice: unsignedTx.gasPrice,
  gasLimit: unsignedTx.gasLimit,
  to: unsignedTx.to,
  value: unsignedTx.value,
  data: unsignedTx.data,
  v,
  r: addHexPrefix(r),
  s: addHexPrefix(s),
}
const signedTx = Tx.fromTxData(toSignTx, { common })
const serializeSignedTx = signedTx.serialize()
const signedTxHex = addHexPrefix(serializeSignedTx.toString('hex'))

web3.eth.sendSignedTransaction(signedTxHex, async function (err, hash) {
    if (!err) {
      console.log('-----hash-------', hash);
      const url = `https://goerli.etherscan.io/tx/${hash}`;
      const accepted = `https://etherscan.io/api?module=localchk&action=txexist&txhash=${hash}`;
      console.log('TX Link', url);
      console.log('Accepted', accepted);
    } else {
       console.log('-----error', err);
    }
})
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容