跳转到主要内容
Injective Ethereum 跨链桥使 Injective Chain 能够支持无需信任的链上双向代币桥接。在这个系统中,Ethereum 上的 ERC-20 代币持有者可以即时将其 ERC-20 代币转换为 Injective Chain 上的 Cosmos 原生代币,反之亦然。 Injective Peggy 跨链桥由三个主要组件组成:
  1. Ethereum 上的 Peggy 合约
  2. Peggo Orchestrator
  3. Injective Chain 上的 Peggy 模块

Peggy 合约

Peggy 合约的功能是促进从 Ethereum 到 Injective Chain 的高效双向 ERC-20 代币跨链转移。与其他代币桥接设置不同,Injective Peggy 跨链桥是一个去中心化、非托管的桥接,完全由 Injective 上的验证者运营。该桥接由 Injective Chain 的权益证明安全性保护,因为存款和取款是根据至少三分之二的验证者基于共识质押权重做出的证明来处理的。

Peggo Orchestrator

Orchestrator 是每个 Injective Chain 验证者运行的链下中继器,其功能是将 ERC-20 代币转移数据从 Ethereum 传输到 Injective Chain。

Peggy 模块

在基本层面上,Peggy 模块在从 Ethereum 存入 ERC-20 时在 Injective Chain 上铸造新代币,并在从 Injective Chain 提取代币回 Ethereum 时销毁代币。Peggy 模块还通过各种机制管理经济激励,以确保验证者诚实高效地行事,包括削减惩罚、原生代币奖励和提款费用。

从 Ethereum 到 Injective

要从 Ethereum 转移到 Injective,你需要进行 Web3 交易并与 Ethereum 上的 Peggy 合约交互。完成转移需要两个步骤:
  1. 由于我们基本上是将 ERC20 资产锁定在 Ethereum 上的 Peggy 合约中,我们需要为要转移到 Peggy 合约的资产设置授权额度。你可以使用任何 web3 提供者(如 ethers.js 或 web3.js)调用 ERC-20 的 approve 函数,将 Peggy 合约地址作为 spender。
  2. 设置授权额度后,我们需要调用 Peggy 合约上的 sendToInjective 函数,指定我们想要转移到 Injective Chain 的金额和资产。你可以使用 @injectivelabs/contracts 中的 PeggyContract 类(见下面的示例)或使用 ABI 直接与合约交互。交易确认后,资产将在几分钟内显示在 Injective Chain 上。
关于上述示例的几点说明:
  • 目标地址(如果你想自己构建交易)格式如下
"0x000000000000000000000000{ETHEREUM_ADDRESS_HERE_WITHOUT_0X_PREFIX}";
// 示例
"0x000000000000000000000000e28b3b32b6c345a34ff64674606124dd5aceca30";
其中 Ethereum 地址是目标 Injective 地址对应的 Ethereum 地址。
  • walletStrategy 是我们构建的一个抽象,支持许多钱包,可用于签名和广播交易(在 Ethereum 和 Injective Chain 上),更多详情可以在 npm 包 @injectivelabs/wallet-strategy@injectivelabs/wallet-core 的文档中找到。显然,这只是一个示例,你可以直接使用 web3 包或任何 web3 提供者来处理交易。
import { PeggyContract } from "@injectivelabs/contracts";

const contract = new PeggyContract({
  ethereumChainId,
  address: peggyContractAddress,
  web3: web3 as any,
});
  • 下面的代码片段实例化了一个 PeggyContract 实例,可以使用我们提供给合约构造函数的 web3 轻松进行 estimateGassendTransaction。其实现可以在这里找到。显然,这只是一个示例,你可以直接使用 web3 包 + 合约的 ABI 来实例化合约,然后使用某个 web3 提供者处理签名和广播交易的逻辑。

从 Injective 到 Ethereum

现在你已经将 INJ 的 ERC20 版本转移到了 Injective,Injective Chain 上的原生 inj denom 被铸造,它是 INJ 代币的规范版本。要从 Injective 提取 inj 到 Ethereum,我们需要准备、签名然后广播一个 Injective Chain 上的原生 Cosmos 交易。 如果你不熟悉 Cosmos 上的交易(和消息)如何工作,可以在这里找到更多信息。我们需要打包到交易中以指示 Injective 从 Injective 提取资金到 Ethereum 的消息是 MsgSendToEth 当在链上调用 MsgSendToEth 时,一些验证者会接收交易,将多个 MsgSendToEth 请求批量处理为一个,然后:在 Injective 上销毁被提取的资产,在 Ethereum 上的 Peggy 智能合约中解锁这些资金并将它们发送到相应的地址。 这些交易中包含一个 bridgeFee,以激励验证者更快地接收和处理你的提款请求。bridgeFee 使用用户想要提取到 Ethereum 的资产(如果你提取 INJ,你也必须用 INJ 支付 bridgeFee)。 以下是一个示例实现,准备交易,使用 privateKey 签名,最后广播到 Injective:
import { getNetworkInfo, Network } from "@injectivelabs/networks";
import {
  TxClient,
  TxRestApi,
  createTransaction,
} from "@injectivelabs/sdk-ts/core/tx";
import {
  PrivateKey,
} from "@injectivelabs/sdk-ts/core/accounts";
import {
  MsgSendToEth,
} from "@injectivelabs/sdk-ts/core/modules";
import {
  ChainRestAuthApi,
} from "@injectivelabs/sdk-ts/client/chain";
import { toChainFormat, getDefaultStdFee } from "@injectivelabs/utils";

/** MsgSendToEth 示例 */
(async () => {
  const network = getNetworkInfo(Network.Mainnet); // 获取 rpc/lcd 端点
  const privateKeyHash =
    "f9db9bf330e23cb7839039e944adef6e9df447b90b503d5b4464c90bea9022f3";
  const privateKey = PrivateKey.fromPrivateKey(privateKeyHash);
  const injectiveAddress = privateKey.toBech32();
  const ethAddress = privateKey.toHex();
  const publicKey = privateKey.toPublicKey().toBase64();

  /** 账户详情 **/
  const accountDetails = await new ChainRestAuthApi(network.rest).fetchAccount(
    injectiveAddress
  );

  /** 准备消息 */
  const amount = {
    amount: toChainFormat(0.01).toFixed(),
    denom: "inj",
  };
  const bridgeFee = {
    amount: toChainFormat(0.01).toFixed(),
    denom: "inj",
  };

  const msg = MsgSendToEth.fromJSON({
    amount,
    bridgeFee,
    injectiveAddress,
    address: ethAddress,
  });

  /** 准备交易 **/
  const { signBytes, txRaw } = createTransaction({
    message: msg,
    pubKey: publicKey,
    fee: getDefaultStdFee(),
    sequence: parseInt(accountDetails.account.base_account.sequence, 10),
    accountNumber: parseInt(
      accountDetails.account.base_account.account_number,
      10
    ),
    chainId: network.chainId,
  });

  /** 签名交易 */
  const signature = await privateKey.sign(Buffer.from(signBytes));

  /** 附加签名 */
  txRaw.signatures = [signature];

  /** 计算交易哈希 */
  console.log(`Transaction Hash: ${TxClient.hash(txRaw)}`);

  const txService = new TxRestApi(network.rest);

  /** 模拟交易 */
  const simulationResponse = await txService.simulate(txRaw);

  console.log(
    `Transaction simulation response: ${JSON.stringify(
      simulationResponse.gasInfo
    )}`
  );

  /** 广播交易 */
  const txResponse = await txService.broadcast(txRaw);

  if (txResponse.code !== 0) {
    console.log(`Transaction failed: ${txResponse.rawLog}`);
  } else {
    console.log(
      `Broadcasted transaction hash: ${JSON.stringify(txResponse.txhash)}`
    );
  }
})();